From 375d8ae670348e4777b8b2965eb21f9c87de680d Mon Sep 17 00:00:00 2001 From: vyuldashev Date: Wed, 31 Jan 2018 18:13:38 +0300 Subject: [PATCH 001/375] prepare for laravel 5.6 --- .travis.yml | 1 + composer.json | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3cee34b0..424aa1d4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,7 @@ language: php php: - 7.0 - 7.1 + - 7.2 env: - RABBITMQ_CONFIG_FILE="/tmp/rabbitmq.config" SSL_CAFILE="/tmp/rootCA.pem diff --git a/composer.json b/composer.json index 477b9bf0..7f43029f 100644 --- a/composer.json +++ b/composer.json @@ -10,15 +10,15 @@ ], "require": { "php": ">=7.0", - "illuminate/database": "5.5.*", - "illuminate/support": "5.5.*", - "illuminate/queue": "5.5.*", - "enqueue/amqp-lib": "0.8.5", + "illuminate/database": "5.6.*", + "illuminate/support": "5.6.*", + "illuminate/queue": "5.6.*", + "enqueue/amqp-lib": "0.8.15", "queue-interop/amqp-interop": "^0.7" }, "require-dev": { "phpunit/phpunit": "~6.0", - "illuminate/events": "5.5.*", + "illuminate/events": "5.6.*", "mockery/mockery": "~1.0" }, "autoload": { From bdc3503de3ea52b86f60282422fc3329c11e196c Mon Sep 17 00:00:00 2001 From: vyuldashev Date: Wed, 31 Jan 2018 18:17:06 +0300 Subject: [PATCH 002/375] prepare for laravel 5.6 --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 424aa1d4..bbf70d3e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,6 @@ dist: xenial language: php php: - - 7.0 - 7.1 - 7.2 From d3e43054c2d200dcf3a9e20e570f6a27ade4b4a3 Mon Sep 17 00:00:00 2001 From: vyuldashev Date: Wed, 31 Jan 2018 18:20:25 +0300 Subject: [PATCH 003/375] prepare for laravel 5.6 --- tests/Functional/SslConnectionTest.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/Functional/SslConnectionTest.php b/tests/Functional/SslConnectionTest.php index 87310f34..503b094a 100644 --- a/tests/Functional/SslConnectionTest.php +++ b/tests/Functional/SslConnectionTest.php @@ -18,6 +18,8 @@ class SslConnectionTest extends TestCase { public function testConnectorEstablishSecureConnectionWithRabbitMQBroker() { + $this->markTestIncomplete(); + $config = [ 'factory_class' => AmqpConnectionFactory::class, 'dsn' => null, From 3ad844e893a8f96234a31bda65fde42d336c4bb3 Mon Sep 17 00:00:00 2001 From: vyuldashev Date: Wed, 31 Jan 2018 18:22:25 +0300 Subject: [PATCH 004/375] prepare for laravel 5.6 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index bbf70d3e..167cc071 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,7 +13,7 @@ services: - rabbitmq before_script: - - COMPOSER_DISCARD_CHANGES=1 composer update --prefer-dist --no-interaction --no-suggest + - COMPOSER_DISCARD_CHANGES=1 composer update --prefer-dist --no-progress --no-interaction --no-suggest - sed -i 's///g' phpunit.xml.dist - openssl genrsa -out /tmp/rootCA.key 2048 - openssl req -x509 -new -nodes -key /tmp/rootCA.key -sha256 -days 1024 -out /tmp/rootCA.pem -subj "/C=US/ST=Arizona/L=Scottsdale/O=Example Company Inc./CN=127.0.0.1" From dfc78124d88549c836c233e475a9fa001f968974 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Thu, 1 Feb 2018 01:00:40 +0300 Subject: [PATCH 005/375] Update README.md --- README.md | 43 ++++++++++++++++--------------------------- 1 file changed, 16 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 18973cb0..5d985909 100644 --- a/README.md +++ b/README.md @@ -6,15 +6,17 @@ RabbitMQ Queue driver for Laravel [![StyleCI](https://styleci.io/repos/14976752/shield)](https://styleci.io/repos/14976752) [![License](https://poser.pugx.org/vladimir-yuldashev/laravel-queue-rabbitmq/license?format=flat-square)](https://packagist.org/packages/vladimir-yuldashev/laravel-queue-rabbitmq) -#### Installation +## Installation -1. Install this package via composer using: +You can install this package via composer using this command: ``` composer require vladimir-yuldashev/laravel-queue-rabbitmq ``` -2. Add these properties to `.env` with proper values: +The package will automatically register itself. + +Add these properties to `.env` with proper values: ``` QUEUE_DRIVER=rabbitmq @@ -30,7 +32,7 @@ RABBITMQ_PASSWORD=guest RABBITMQ_QUEUE=queue_name ``` -3. Optionally: if you want to to use an SSL connection, add these properties to the `.env` with proper values: +Optionally, if you want to to use an SSL connection, add these properties to the `.env` with proper values: ``` RABBITMQ_SSL=true RABBITMQ_SSL_CAFILE=/path_to_your_ca_file @@ -41,55 +43,42 @@ RABBITMQ_SSL_KEY= Using an SSL connection will also require to configure your RabbitMQ to enable SSL. More details can be founds here: https://www.rabbitmq.com/ssl.html -4. Other AMQP transports +## Using AMQP transports The package uses [enqueue/amqp-lib](https://github.com/php-enqueue/enqueue-dev/blob/master/docs/transport/amqp_lib.md) transport which is based on [php-amqplib](https://github.com/php-amqplib/php-amqplib). There is possibility to use any [amqp interop](https://github.com/queue-interop/queue-interop#amqp-interop) compatible transport, for example `enqueue/amqp-ext` or `enqueue/amqp-bunny`. Here's an example on how one can change the transport to `enqueue/amqp-bunny`. -First, install the package: +First, install desired transport package: ```bash -$ composer require enqueue/amqp-bunny:^0.8 +composer require enqueue/amqp-bunny:^0.8 ``` -and change the factory class: +Change the factory class in `config/queue.php`: ```php - [ 'rabbitmq' => [ 'driver' => 'rabbitmq', - 'factory_class' => \Enqueue\AmqpBunny\AmqpConnectionFactory::class, + 'factory_class' => Enqueue\AmqpBunny\AmqpConnectionFactory::class, ], ], -]; ``` -#### Usage +## Usage Once you completed the configuration you can use Laravel Queue API. If you used other queue drivers you do not need to change anything else. If you do not know how to use Queue API, please refer to the official Laravel documentation: http://laravel.com/docs/queues -#### Testing +## Testing -Run the tests with: +You can run the tests with: ``` bash vendor/bin/phpunit ``` - -#### Contribution +## Contribution You can contribute to this package by discovering bugs and opening issues. Please, add to which version of package you create pull request or issue. (e.g. [5.2] Fatal error on delayed job) - -> If you want to make feature for several versions (for example: 5.2, 5.3, 5.4 and 5.5). Create PR for the lowest version (5.2). Hence, you should use branch v5.2. - -#### Supported versions of Laravel (+Lumen) - -`4.0, 4.1, 4.2, 5.0, 5.1, 5.2, 5.3, 5.4, 5.5` - -The version is being matched by the release tag of this library. From f25a01203caa52a8b914bb3e78343686211c67d0 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Thu, 1 Feb 2018 01:08:11 +0300 Subject: [PATCH 006/375] Update README.md --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 5d985909..3d82c83d 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,11 @@ RABBITMQ_SSL_KEY= Using an SSL connection will also require to configure your RabbitMQ to enable SSL. More details can be founds here: https://www.rabbitmq.com/ssl.html -## Using AMQP transports +## Usage + +Once you completed the configuration you can use Laravel Queue API. If you used other queue drivers you do not need to change anything else. If you do not know how to use Queue API, please refer to the official Laravel documentation: http://laravel.com/docs/queues + +## Using other AMQP transports The package uses [enqueue/amqp-lib](https://github.com/php-enqueue/enqueue-dev/blob/master/docs/transport/amqp_lib.md) transport which is based on [php-amqplib](https://github.com/php-amqplib/php-amqplib). There is possibility to use any [amqp interop](https://github.com/queue-interop/queue-interop#amqp-interop) compatible transport, for example `enqueue/amqp-ext` or `enqueue/amqp-bunny`. @@ -67,10 +71,6 @@ Change the factory class in `config/queue.php`: ], ``` -## Usage - -Once you completed the configuration you can use Laravel Queue API. If you used other queue drivers you do not need to change anything else. If you do not know how to use Queue API, please refer to the official Laravel documentation: http://laravel.com/docs/queues - ## Testing You can run the tests with: From 78d1ed8690453f0ab4b3f2cb4dabbcb8bb8b6d61 Mon Sep 17 00:00:00 2001 From: vyuldashev Date: Mon, 12 Feb 2018 00:21:45 +0300 Subject: [PATCH 007/375] update phpunit to 7.0 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 7f43029f..4bee390c 100644 --- a/composer.json +++ b/composer.json @@ -17,7 +17,7 @@ "queue-interop/amqp-interop": "^0.7" }, "require-dev": { - "phpunit/phpunit": "~6.0", + "phpunit/phpunit": "~7.0", "illuminate/events": "5.6.*", "mockery/mockery": "~1.0" }, From fd1fc13821628d73f275e1b77daf78cdf78b2195 Mon Sep 17 00:00:00 2001 From: vyuldashev Date: Mon, 12 Feb 2018 00:22:14 +0300 Subject: [PATCH 008/375] change branch alias --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 4bee390c..28e9df84 100644 --- a/composer.json +++ b/composer.json @@ -33,7 +33,7 @@ }, "extra": { "branch-alias": { - "dev-master": "6.0-dev" + "dev-master": "7.0-dev" }, "laravel": { "providers": [ From 075b2ddbc7862f8517a720a1ad298498ff3b6869 Mon Sep 17 00:00:00 2001 From: vyuldashev Date: Mon, 12 Mar 2018 14:57:43 +0300 Subject: [PATCH 009/375] update amqplib version --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 28e9df84..75074fbb 100644 --- a/composer.json +++ b/composer.json @@ -13,7 +13,7 @@ "illuminate/database": "5.6.*", "illuminate/support": "5.6.*", "illuminate/queue": "5.6.*", - "enqueue/amqp-lib": "0.8.15", + "enqueue/amqp-lib": "0.8.*", "queue-interop/amqp-interop": "^0.7" }, "require-dev": { From 066889760dbf3f55f5afa6b4754e81d9c9297953 Mon Sep 17 00:00:00 2001 From: Vlad Kucherov Date: Thu, 10 May 2018 11:17:53 +0300 Subject: [PATCH 010/375] Adds support for custom headers and properties on pushRaw --- src/Queue/RabbitMQQueue.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index d8923208..93fe5dd7 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -74,6 +74,14 @@ public function pushRaw($payload, $queueName = null, array $options = []) $message->setCorrelationId($this->getCorrelationId()); $message->setContentType('application/json'); $message->setDeliveryMode(AmqpMessage::DELIVERY_MODE_PERSISTENT); + + if (isset($options['headers'])) { + $message->setHeaders($options['headers']); + } + + if (isset($options['properties'])) { + $message->setProperties($options['properties']); + } if (isset($options['attempts'])) { $message->setProperty(RabbitMQJob::ATTEMPT_COUNT_HEADERS_KEY, $options['attempts']); From 9764821572f29d2c28c5e5a6654efe70a766770e Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Wed, 30 May 2018 18:13:18 +0300 Subject: [PATCH 011/375] resolve #187 issue --- config/rabbitmq.php | 7 ++----- src/Queue/RabbitMQQueue.php | 4 +++- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/config/rabbitmq.php b/config/rabbitmq.php index 207d9e11..3e0000e5 100644 --- a/config/rabbitmq.php +++ b/config/rabbitmq.php @@ -26,6 +26,8 @@ 'login' => env('RABBITMQ_LOGIN', 'guest'), 'password' => env('RABBITMQ_PASSWORD', 'guest'), + 'queue' => env('RABBITMQ_QUEUE', 'default'), + 'options' => [ 'exchange' => [ @@ -49,11 +51,6 @@ 'queue' => [ - /* - * The name of default queue. - */ - 'name' => env('RABBITMQ_QUEUE', 'default'), - /* * Determine if queue should be created if it does not exist. */ diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index 93fe5dd7..bbe12c1a 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -17,6 +17,7 @@ class RabbitMQQueue extends Queue implements QueueContract { protected $sleepOnError; + protected $queueName; protected $queueOptions; protected $exchangeOptions; @@ -33,6 +34,7 @@ public function __construct(AmqpContext $context, array $config) { $this->context = $context; + $this->queueName = $config['queue'] ?? $config['options']['queue']['name']; $this->queueOptions = $config['options']['queue']; $this->queueOptions['arguments'] = isset($this->queueOptions['arguments']) ? json_decode($this->queueOptions['arguments'], true) : []; @@ -182,7 +184,7 @@ public function getContext(): AmqpContext */ private function declareEverything(string $queueName = null): array { - $queueName = $queueName ?: $this->queueOptions['name']; + $queueName = $queueName ?: $this->queueName; $exchangeName = $this->exchangeOptions['name'] ?: $queueName; $topic = $this->context->createTopic($exchangeName); From 5ab28ca6f167a851eaccd9f3281e2d78e531a962 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Fri, 15 Jun 2018 18:07:29 +0300 Subject: [PATCH 012/375] Update composer.json --- composer.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index 75074fbb..e2d75bcd 100644 --- a/composer.json +++ b/composer.json @@ -9,7 +9,7 @@ } ], "require": { - "php": ">=7.0", + "php": "^7.1.3", "illuminate/database": "5.6.*", "illuminate/support": "5.6.*", "illuminate/queue": "5.6.*", @@ -17,9 +17,9 @@ "queue-interop/amqp-interop": "^0.7" }, "require-dev": { - "phpunit/phpunit": "~7.0", + "phpunit/phpunit": "^7.0", "illuminate/events": "5.6.*", - "mockery/mockery": "~1.0" + "mockery/mockery": "^1.0" }, "autoload": { "psr-4": { From bc5cc254140223627eb69ab1de7053fd00a63eae Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Tue, 4 Sep 2018 17:05:05 +0300 Subject: [PATCH 013/375] 5.7 --- composer.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/composer.json b/composer.json index e2d75bcd..5dcc739e 100644 --- a/composer.json +++ b/composer.json @@ -10,15 +10,15 @@ ], "require": { "php": "^7.1.3", - "illuminate/database": "5.6.*", - "illuminate/support": "5.6.*", - "illuminate/queue": "5.6.*", + "illuminate/database": "5.6.*|5.7.*", + "illuminate/support": "5.6.*|5.7.*", + "illuminate/queue": "5.6.*|5.7.*", "enqueue/amqp-lib": "0.8.*", "queue-interop/amqp-interop": "^0.7" }, "require-dev": { "phpunit/phpunit": "^7.0", - "illuminate/events": "5.6.*", + "illuminate/events": "5.6.*|5.7.*", "mockery/mockery": "^1.0" }, "autoload": { From 3d24fca8719e93f4705aaa4f2869f91ae4b47a9a Mon Sep 17 00:00:00 2001 From: Steve Porter Date: Tue, 11 Sep 2018 21:21:26 +0100 Subject: [PATCH 014/375] change method visibility to enable extension --- src/Queue/Jobs/RabbitMQJob.php | 2 +- src/Queue/RabbitMQQueue.php | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Queue/Jobs/RabbitMQJob.php b/src/Queue/Jobs/RabbitMQJob.php index 92d713d3..12d15568 100644 --- a/src/Queue/Jobs/RabbitMQJob.php +++ b/src/Queue/Jobs/RabbitMQJob.php @@ -155,7 +155,7 @@ public function setJobId($id) * * @return mixed */ - private function unserialize(array $body) + protected function unserialize(array $body) { try { /** @noinspection UnserializeExploitsInspection */ diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index bbe12c1a..ab03c9a4 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -21,14 +21,14 @@ class RabbitMQQueue extends Queue implements QueueContract protected $queueOptions; protected $exchangeOptions; - private $declaredExchanges = []; - private $declaredQueues = []; + protected $declaredExchanges = []; + protected $declaredQueues = []; /** * @var AmqpContext */ - private $context; - private $correlationId; + protected $context; + protected $correlationId; public function __construct(AmqpContext $context, array $config) { @@ -182,7 +182,7 @@ public function getContext(): AmqpContext * * @return array [Interop\Amqp\AmqpQueue, Interop\Amqp\AmqpTopic] */ - private function declareEverything(string $queueName = null): array + protected function declareEverything(string $queueName = null): array { $queueName = $queueName ?: $this->queueName; $exchangeName = $this->exchangeOptions['name'] ?: $queueName; From c3ffcea264d8ccdacf51344e1e134c39ed69326a Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Wed, 12 Sep 2018 01:05:43 +0300 Subject: [PATCH 015/375] update README --- README.md | 129 +++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 103 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 3d82c83d..3a70d032 100644 --- a/README.md +++ b/README.md @@ -14,34 +14,111 @@ You can install this package via composer using this command: composer require vladimir-yuldashev/laravel-queue-rabbitmq ``` -The package will automatically register itself. +The package will automatically register itself using Laravel auto-discovery. -Add these properties to `.env` with proper values: +Setup connection in `config/queue.php` +```php +'connections' => [ + // ... + 'rabbitmq' => [ + + 'driver' => 'rabbitmq', + + 'dsn' => env('RABBITMQ_DSN', null), + + /* + * Could be one a class that implements \Interop\Amqp\AmqpConnectionFactory for example: + * - \EnqueueAmqpExt\AmqpConnectionFactory if you install enqueue/amqp-ext + * - \EnqueueAmqpLib\AmqpConnectionFactory if you install enqueue/amqp-lib + * - \EnqueueAmqpBunny\AmqpConnectionFactory if you install enqueue/amqp-bunny + */ + + 'factory_class' => Enqueue\AmqpLib\AmqpConnectionFactory::class, + + 'host' => env('RABBITMQ_HOST', '127.0.0.1'), + 'port' => env('RABBITMQ_PORT', 5672), + + 'vhost' => env('RABBITMQ_VHOST', '/'), + 'login' => env('RABBITMQ_LOGIN', 'guest'), + 'password' => env('RABBITMQ_PASSWORD', 'guest'), + + 'queue' => env('RABBITMQ_QUEUE', 'default'), + + 'options' => [ + + 'exchange' => [ + + 'name' => env('RABBITMQ_EXCHANGE_NAME'), + + /* + * Determine if exchange should be created if it does not exist. + */ + + 'declare' => env('RABBITMQ_EXCHANGE_DECLARE', true), + + /* + * Read more about possible values at https://www.rabbitmq.com/tutorials/amqp-concepts.html + */ + + 'type' => env('RABBITMQ_EXCHANGE_TYPE', \Interop\Amqp\AmqpTopic::TYPE_DIRECT), + 'passive' => env('RABBITMQ_EXCHANGE_PASSIVE', false), + 'durable' => env('RABBITMQ_EXCHANGE_DURABLE', true), + 'auto_delete' => env('RABBITMQ_EXCHANGE_AUTODELETE', false), + 'arguments' => env('RABBITMQ_EXCHANGE_ARGUMENTS'), + ], + + 'queue' => [ + + /* + * Determine if queue should be created if it does not exist. + */ + + 'declare' => env('RABBITMQ_QUEUE_DECLARE', true), + + /* + * Determine if queue should be binded to the exchange created. + */ + + 'bind' => env('RABBITMQ_QUEUE_DECLARE_BIND', true), + + /* + * Read more about possible values at https://www.rabbitmq.com/tutorials/amqp-concepts.html + */ + + 'passive' => env('RABBITMQ_QUEUE_PASSIVE', false), + 'durable' => env('RABBITMQ_QUEUE_DURABLE', true), + 'exclusive' => env('RABBITMQ_QUEUE_EXCLUSIVE', false), + 'auto_delete' => env('RABBITMQ_QUEUE_AUTODELETE', false), + 'arguments' => env('RABBITMQ_QUEUE_ARGUMENTS'), + ], + ], + + /* + * Determine the number of seconds to sleep if there's an error communicating with rabbitmq + * If set to false, it'll throw an exception rather than doing the sleep for X seconds. + */ + + 'sleep_on_error' => env('RABBITMQ_ERROR_SLEEP', 5), + + /* + * Optional SSL params if an SSL connection is used + * Using an SSL connection will also require to configure your RabbitMQ to enable SSL. More details can be founds here: https://www.rabbitmq.com/ssl.html + */ + + 'ssl_params' => [ + 'ssl_on' => env('RABBITMQ_SSL', false), + 'cafile' => env('RABBITMQ_SSL_CAFILE', null), + 'local_cert' => env('RABBITMQ_SSL_LOCALCERT', null), + 'local_key' => env('RABBITMQ_SSL_LOCALKEY', null), + 'verify_peer' => env('RABBITMQ_SSL_VERIFY_PEER', true), + 'passphrase' => env('RABBITMQ_SSL_PASSPHRASE', null), + ], + + ], + // ... +], ``` -QUEUE_DRIVER=rabbitmq -RABBITMQ_QUEUE=queue_name - -RABBITMQ_DSN=amqp: # same as amqp://guest:guest@127.0.0.1:5672/%2F -# or -RABBITMQ_HOST=127.0.0.1 -RABBITMQ_PORT=5672 -RABBITMQ_VHOST=/ -RABBITMQ_LOGIN=guest -RABBITMQ_PASSWORD=guest -RABBITMQ_QUEUE=queue_name -``` - -Optionally, if you want to to use an SSL connection, add these properties to the `.env` with proper values: -``` -RABBITMQ_SSL=true -RABBITMQ_SSL_CAFILE=/path_to_your_ca_file -RABBITMQ_SSL_LOCALCERT= -RABBITMQ_SSL_PASSPHRASE= -RABBITMQ_SSL_KEY= -``` - -Using an SSL connection will also require to configure your RabbitMQ to enable SSL. More details can be founds here: https://www.rabbitmq.com/ssl.html ## Usage @@ -62,7 +139,7 @@ composer require enqueue/amqp-bunny:^0.8 Change the factory class in `config/queue.php`: ```php -... + // ... 'connections' => [ 'rabbitmq' => [ 'driver' => 'rabbitmq', From b8033360f24f06b5b8128fbbc4f5a308edb7db9f Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Mon, 10 Dec 2018 02:00:09 +0300 Subject: [PATCH 016/375] code improvements --- composer.json | 1 + src/Queue/RabbitMQQueue.php | 28 ++++++++++++++-------------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/composer.json b/composer.json index 5dcc739e..4d955dc1 100644 --- a/composer.json +++ b/composer.json @@ -10,6 +10,7 @@ ], "require": { "php": "^7.1.3", + "ext-json": "*", "illuminate/database": "5.6.*|5.7.*", "illuminate/support": "5.6.*|5.7.*", "illuminate/queue": "5.6.*|5.7.*", diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index ab03c9a4..dba78661 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -50,7 +50,7 @@ public function __construct(AmqpContext $context, array $config) public function size($queueName = null): int { /** @var AmqpQueue $queue */ - list($queue) = $this->declareEverything($queueName); + [$queue] = $this->declareEverything($queueName); return $this->context->declareQueue($queue); } @@ -69,18 +69,18 @@ public function pushRaw($payload, $queueName = null, array $options = []) * @var AmqpTopic $topic * @var AmqpQueue $queue */ - list($queue, $topic) = $this->declareEverything($queueName); + [$queue, $topic] = $this->declareEverything($queueName); $message = $this->context->createMessage($payload); $message->setRoutingKey($queue->getQueueName()); $message->setCorrelationId($this->getCorrelationId()); $message->setContentType('application/json'); $message->setDeliveryMode(AmqpMessage::DELIVERY_MODE_PERSISTENT); - + if (isset($options['headers'])) { $message->setHeaders($options['headers']); } - + if (isset($options['properties'])) { $message->setProperties($options['properties']); } @@ -113,11 +113,11 @@ public function later($delay, $job, $data = '', $queue = null) /** * Release a reserved job back onto the queue. * - * @param \DateTimeInterface|\DateInterval|int $delay - * @param string|object $job - * @param mixed $data - * @param string $queue - * @param int $attempts + * @param \DateTimeInterface|\DateInterval|int $delay + * @param string|object $job + * @param mixed $data + * @param string $queue + * @param int $attempts * @return mixed */ public function release($delay, $job, $data, $queue, $attempts = 0) @@ -133,14 +133,14 @@ public function pop($queueName = null) { try { /** @var AmqpQueue $queue */ - list($queue) = $this->declareEverything($queueName); + [$queue] = $this->declareEverything($queueName); $consumer = $this->context->createConsumer($queue); if ($message = $consumer->receiveNoWait()) { return new RabbitMQJob($this->container, $this, $consumer, $message); } - } catch (\Exception $exception) { + } catch (\Throwable $exception) { $this->reportConnectionError('pop', $exception); } @@ -164,7 +164,7 @@ public function getCorrelationId(): string * * @return void */ - public function setCorrelationId(string $id) + public function setCorrelationId(string $id): void { $this->correlationId = $id; } @@ -236,10 +236,10 @@ protected function declareEverything(string $queueName = null): array /** * @param string $action - * @param \Exception $e + * @param \Throwable $e * @throws \Exception */ - protected function reportConnectionError($action, \Exception $e) + protected function reportConnectionError($action, \Throwable $e) { /** @var LoggerInterface $logger */ $logger = $this->container['log']; From b87110bed9c1389edd9e1477749e750b0b56545c Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Mon, 10 Dec 2018 02:13:50 +0300 Subject: [PATCH 017/375] travis --- .travis.yml | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/.travis.yml b/.travis.yml index 167cc071..3ac764f0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,7 @@ language: php php: - 7.1 - 7.2 + - 7.3 env: - RABBITMQ_CONFIG_FILE="/tmp/rabbitmq.config" SSL_CAFILE="/tmp/rootCA.pem @@ -14,17 +15,17 @@ services: before_script: - COMPOSER_DISCARD_CHANGES=1 composer update --prefer-dist --no-progress --no-interaction --no-suggest - - sed -i 's///g' phpunit.xml.dist - - openssl genrsa -out /tmp/rootCA.key 2048 - - openssl req -x509 -new -nodes -key /tmp/rootCA.key -sha256 -days 1024 -out /tmp/rootCA.pem -subj "/C=US/ST=Arizona/L=Scottsdale/O=Example Company Inc./CN=127.0.0.1" - - openssl genrsa -out /tmp/device.key 2048 - - openssl req -new -key /tmp/device.key -out /tmp/device.csr -subj "/C=US/ST=Arizona/L=Scottsdale/O=Example Company Inc./CN=127.0.0.1" - - openssl x509 -req -in /tmp/device.csr -CA /tmp/rootCA.pem -CAkey /tmp/rootCA.key -CAcreateserial -out /tmp/device.crt -days 500 -sha256 - - sudo service rabbitmq-server stop - - echo "[{rabbit,[{ssl_listeners, [5671]},{ssl_options,[{cacertfile,\"/tmp/rootCA.pem\"},{certfile,\"/tmp/rootCA.pem\"},{keyfile,\"/tmp/rootCA.key\"},{verify,verify_none},{fail_if_no_peer_cert,false}]}]}]." > /tmp/rabbitmq.config - - cp /tmp/rabbitmq.config /tmp/rabbitmq.config.config - - sudo rabbitmq-server & - - sleep 10 +# - sed -i 's///g' phpunit.xml.dist +# - openssl genrsa -out /tmp/rootCA.key 2048 +# - openssl req -x509 -new -nodes -key /tmp/rootCA.key -sha256 -days 1024 -out /tmp/rootCA.pem -subj "/C=US/ST=Arizona/L=Scottsdale/O=Example Company Inc./CN=127.0.0.1" +# - openssl genrsa -out /tmp/device.key 2048 +# - openssl req -new -key /tmp/device.key -out /tmp/device.csr -subj "/C=US/ST=Arizona/L=Scottsdale/O=Example Company Inc./CN=127.0.0.1" +# - openssl x509 -req -in /tmp/device.csr -CA /tmp/rootCA.pem -CAkey /tmp/rootCA.key -CAcreateserial -out /tmp/device.crt -days 500 -sha256 +# - sudo service rabbitmq-server stop +# - echo "[{rabbit,[{ssl_listeners, [5671]},{ssl_options,[{cacertfile,\"/tmp/rootCA.pem\"},{certfile,\"/tmp/rootCA.pem\"},{keyfile,\"/tmp/rootCA.key\"},{verify,verify_none},{fail_if_no_peer_cert,false}]}]}]." > /tmp/rabbitmq.config +# - cp /tmp/rabbitmq.config /tmp/rabbitmq.config.config +# - sudo rabbitmq-server & +# - sleep 10 script: - - composer test \ No newline at end of file + - composer test From d90d54cb50eb1fca9fc6c971f714d1d6cf938680 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Mon, 10 Dec 2018 02:14:40 +0300 Subject: [PATCH 018/375] travis --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3ac764f0..4a12663c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,8 +7,8 @@ php: - 7.2 - 7.3 -env: - - RABBITMQ_CONFIG_FILE="/tmp/rabbitmq.config" SSL_CAFILE="/tmp/rootCA.pem +#env: +# - RABBITMQ_CONFIG_FILE="/tmp/rabbitmq.config" SSL_CAFILE="/tmp/rootCA.pem services: - rabbitmq From 70de25b693d1f6d673a7f918c326c77817693778 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Mon, 10 Dec 2018 02:24:54 +0300 Subject: [PATCH 019/375] travis --- .travis.yml | 3 ++- composer.json | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4a12663c..3b5ec397 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,10 +11,11 @@ php: # - RABBITMQ_CONFIG_FILE="/tmp/rabbitmq.config" SSL_CAFILE="/tmp/rootCA.pem services: - - rabbitmq + - docker before_script: - COMPOSER_DISCARD_CHANGES=1 composer update --prefer-dist --no-progress --no-interaction --no-suggest + - docker run -d rabbitmq:3 # - sed -i 's///g' phpunit.xml.dist # - openssl genrsa -out /tmp/rootCA.key 2048 # - openssl req -x509 -new -nodes -key /tmp/rootCA.key -sha256 -days 1024 -out /tmp/rootCA.pem -subj "/C=US/ST=Arizona/L=Scottsdale/O=Example Company Inc./CN=127.0.0.1" diff --git a/composer.json b/composer.json index 4d955dc1..14f154fb 100644 --- a/composer.json +++ b/composer.json @@ -14,8 +14,8 @@ "illuminate/database": "5.6.*|5.7.*", "illuminate/support": "5.6.*|5.7.*", "illuminate/queue": "5.6.*|5.7.*", - "enqueue/amqp-lib": "0.8.*", - "queue-interop/amqp-interop": "^0.7" + "enqueue/amqp-lib": "0.9.*", + "queue-interop/amqp-interop": "^0.8" }, "require-dev": { "phpunit/phpunit": "^7.0", From 8f6e12acfaec7c206e16f43b79d943dd69b6362f Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Mon, 10 Dec 2018 02:25:48 +0300 Subject: [PATCH 020/375] travis --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 3b5ec397..b10e3e1c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,7 +15,7 @@ services: before_script: - COMPOSER_DISCARD_CHANGES=1 composer update --prefer-dist --no-progress --no-interaction --no-suggest - - docker run -d rabbitmq:3 + - docker run -d -p 5672:5672 rabbitmq:3 # - sed -i 's///g' phpunit.xml.dist # - openssl genrsa -out /tmp/rootCA.key 2048 # - openssl req -x509 -new -nodes -key /tmp/rootCA.key -sha256 -days 1024 -out /tmp/rootCA.pem -subj "/C=US/ST=Arizona/L=Scottsdale/O=Example Company Inc./CN=127.0.0.1" From 90a6516f8adde40569b4600f27dc3c143e7d5d0f Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Mon, 10 Dec 2018 02:28:01 +0300 Subject: [PATCH 021/375] travis --- .travis.yml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index b10e3e1c..4cf7e913 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,8 +13,18 @@ php: services: - docker +cache: + directories: + - $HOME/.composer/cache + +env: + matrix: + - COMPOSER_FLAGS="--prefer-lowest" + - COMPOSER_FLAGS="" + before_script: - - COMPOSER_DISCARD_CHANGES=1 composer update --prefer-dist --no-progress --no-interaction --no-suggest + - travis_retry composer self-update + - travis_retry composer update ${COMPOSER_FLAGS} --no-interaction --prefer-dist - docker run -d -p 5672:5672 rabbitmq:3 # - sed -i 's///g' phpunit.xml.dist # - openssl genrsa -out /tmp/rootCA.key 2048 From 044ff53ff858a083ee4bd8b6c486405bbb882bc1 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Mon, 10 Dec 2018 02:30:22 +0300 Subject: [PATCH 022/375] travis --- composer.json | 4 ++-- phpunit.xml.dist | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/composer.json b/composer.json index 14f154fb..4d955dc1 100644 --- a/composer.json +++ b/composer.json @@ -14,8 +14,8 @@ "illuminate/database": "5.6.*|5.7.*", "illuminate/support": "5.6.*|5.7.*", "illuminate/queue": "5.6.*|5.7.*", - "enqueue/amqp-lib": "0.9.*", - "queue-interop/amqp-interop": "^0.8" + "enqueue/amqp-lib": "0.8.*", + "queue-interop/amqp-interop": "^0.7" }, "require-dev": { "phpunit/phpunit": "^7.0", diff --git a/phpunit.xml.dist b/phpunit.xml.dist index ed6096f3..ef58e0a4 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -10,7 +10,6 @@ processIsolation="false" stopOnError="false" stopOnFailure="false" - syntaxCheck="true" verbose="true" > @@ -30,6 +29,6 @@ - + - \ No newline at end of file + From bb8c59c895fc9a3e5191fc8daf811ab6f1b8fe90 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Mon, 10 Dec 2018 02:36:08 +0300 Subject: [PATCH 023/375] travis --- .travis.yml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4cf7e913..e43defe3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,14 +17,9 @@ cache: directories: - $HOME/.composer/cache -env: - matrix: - - COMPOSER_FLAGS="--prefer-lowest" - - COMPOSER_FLAGS="" - before_script: - travis_retry composer self-update - - travis_retry composer update ${COMPOSER_FLAGS} --no-interaction --prefer-dist + - travis_retry composer update --no-interaction --prefer-dist - docker run -d -p 5672:5672 rabbitmq:3 # - sed -i 's///g' phpunit.xml.dist # - openssl genrsa -out /tmp/rootCA.key 2048 From d7deb45527e93cb75d4c3d04f9a509d9e610f56a Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Mon, 10 Dec 2018 02:38:13 +0300 Subject: [PATCH 024/375] travis --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index e43defe3..051db387 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,7 +19,7 @@ cache: before_script: - travis_retry composer self-update - - travis_retry composer update --no-interaction --prefer-dist + - travis_retry composer update --no-progress --no-interaction --prefer-dist - docker run -d -p 5672:5672 rabbitmq:3 # - sed -i 's///g' phpunit.xml.dist # - openssl genrsa -out /tmp/rootCA.key 2048 From e88d98bfe48d652438bad087bef48448c4cb754e Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Mon, 10 Dec 2018 02:42:07 +0300 Subject: [PATCH 025/375] travis --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 051db387..6675c899 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,7 +11,7 @@ php: # - RABBITMQ_CONFIG_FILE="/tmp/rabbitmq.config" SSL_CAFILE="/tmp/rootCA.pem services: - - docker + - rabbitmq cache: directories: @@ -20,7 +20,6 @@ cache: before_script: - travis_retry composer self-update - travis_retry composer update --no-progress --no-interaction --prefer-dist - - docker run -d -p 5672:5672 rabbitmq:3 # - sed -i 's///g' phpunit.xml.dist # - openssl genrsa -out /tmp/rootCA.key 2048 # - openssl req -x509 -new -nodes -key /tmp/rootCA.key -sha256 -days 1024 -out /tmp/rootCA.pem -subj "/C=US/ST=Arizona/L=Scottsdale/O=Example Company Inc./CN=127.0.0.1" From 71847ceb20fcdc2c192423cbe491831a3f53c191 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Mon, 10 Dec 2018 02:48:03 +0300 Subject: [PATCH 026/375] travis --- .travis.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6675c899..26e6d480 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,3 @@ -dist: xenial - language: php php: From 0203bf5aa013bb22574f990f235ff6e37326ed27 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Mon, 10 Dec 2018 02:50:37 +0300 Subject: [PATCH 027/375] travis --- .travis.yml | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/.travis.yml b/.travis.yml index 26e6d480..b6eb16d2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,8 +5,8 @@ php: - 7.2 - 7.3 -#env: -# - RABBITMQ_CONFIG_FILE="/tmp/rabbitmq.config" SSL_CAFILE="/tmp/rootCA.pem +env: + - RABBITMQ_CONFIG_FILE="/tmp/rabbitmq.config" SSL_CAFILE="/tmp/rootCA.pem services: - rabbitmq @@ -18,17 +18,17 @@ cache: before_script: - travis_retry composer self-update - travis_retry composer update --no-progress --no-interaction --prefer-dist -# - sed -i 's///g' phpunit.xml.dist -# - openssl genrsa -out /tmp/rootCA.key 2048 -# - openssl req -x509 -new -nodes -key /tmp/rootCA.key -sha256 -days 1024 -out /tmp/rootCA.pem -subj "/C=US/ST=Arizona/L=Scottsdale/O=Example Company Inc./CN=127.0.0.1" -# - openssl genrsa -out /tmp/device.key 2048 -# - openssl req -new -key /tmp/device.key -out /tmp/device.csr -subj "/C=US/ST=Arizona/L=Scottsdale/O=Example Company Inc./CN=127.0.0.1" -# - openssl x509 -req -in /tmp/device.csr -CA /tmp/rootCA.pem -CAkey /tmp/rootCA.key -CAcreateserial -out /tmp/device.crt -days 500 -sha256 -# - sudo service rabbitmq-server stop -# - echo "[{rabbit,[{ssl_listeners, [5671]},{ssl_options,[{cacertfile,\"/tmp/rootCA.pem\"},{certfile,\"/tmp/rootCA.pem\"},{keyfile,\"/tmp/rootCA.key\"},{verify,verify_none},{fail_if_no_peer_cert,false}]}]}]." > /tmp/rabbitmq.config -# - cp /tmp/rabbitmq.config /tmp/rabbitmq.config.config -# - sudo rabbitmq-server & -# - sleep 10 + - sed -i 's///g' phpunit.xml.dist + - openssl genrsa -out /tmp/rootCA.key 2048 + - openssl req -x509 -new -nodes -key /tmp/rootCA.key -sha256 -days 1024 -out /tmp/rootCA.pem -subj "/C=US/ST=Arizona/L=Scottsdale/O=Example Company Inc./CN=127.0.0.1" + - openssl genrsa -out /tmp/device.key 2048 + - openssl req -new -key /tmp/device.key -out /tmp/device.csr -subj "/C=US/ST=Arizona/L=Scottsdale/O=Example Company Inc./CN=127.0.0.1" + - openssl x509 -req -in /tmp/device.csr -CA /tmp/rootCA.pem -CAkey /tmp/rootCA.key -CAcreateserial -out /tmp/device.crt -days 500 -sha256 + - sudo service rabbitmq-server stop + - echo "[{rabbit,[{ssl_listeners, [5671]},{ssl_options,[{cacertfile,\"/tmp/rootCA.pem\"},{certfile,\"/tmp/rootCA.pem\"},{keyfile,\"/tmp/rootCA.key\"},{verify,verify_none},{fail_if_no_peer_cert,false}]}]}]." > /tmp/rabbitmq.config + - cp /tmp/rabbitmq.config /tmp/rabbitmq.config.config + - sudo rabbitmq-server & + - sleep 10 script: - composer test From d0c95670dc8a4018f588df9ad346fc564e5255d0 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Mon, 10 Dec 2018 02:52:56 +0300 Subject: [PATCH 028/375] travis --- tests/Functional/SslConnectionTest.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/Functional/SslConnectionTest.php b/tests/Functional/SslConnectionTest.php index 503b094a..87310f34 100644 --- a/tests/Functional/SslConnectionTest.php +++ b/tests/Functional/SslConnectionTest.php @@ -18,8 +18,6 @@ class SslConnectionTest extends TestCase { public function testConnectorEstablishSecureConnectionWithRabbitMQBroker() { - $this->markTestIncomplete(); - $config = [ 'factory_class' => AmqpConnectionFactory::class, 'dsn' => null, From f60e12af4a1a9bba9bacd1a8ba641fe2da3364c5 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Mon, 10 Dec 2018 02:54:35 +0300 Subject: [PATCH 029/375] wip --- .styleci.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 .styleci.yml diff --git a/.styleci.yml b/.styleci.yml new file mode 100644 index 00000000..0285f179 --- /dev/null +++ b/.styleci.yml @@ -0,0 +1 @@ +preset: laravel From d7b49f607931b7812c02e9bc95af3b3566908ff3 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Sun, 9 Dec 2018 23:55:14 +0000 Subject: [PATCH 030/375] Apply fixes from StyleCI --- config/rabbitmq.php | 2 +- src/LaravelQueueRabbitMQServiceProvider.php | 4 +- src/Queue/Connectors/RabbitMQConnector.php | 12 +- src/Queue/Jobs/RabbitMQJob.php | 18 +-- src/Queue/RabbitMQQueue.php | 34 +++-- .../SendAndReceiveDelayedMessageTest.php | 15 +- .../Functional/SendAndReceiveMessageTest.php | 15 +- tests/Functional/SslConnectionTest.php | 9 +- tests/Functional/StreamConnectionTest.php | 9 +- ...aravelQueueRabbitMQServiceProviderTest.php | 14 +- tests/Mock/AmqpConnectionFactorySpy.php | 1 + tests/Mock/AmqpContextMock.php | 5 +- ...CustomContextAmqpConnectionFactoryMock.php | 1 + ...yStrategyAwareAmqpConnectionFactorySpy.php | 1 + .../Connectors/RabbitMQConnectorTest.php | 16 +-- tests/Queue/Jobs/RabbitMQJobTest.php | 22 ++- tests/Queue/RabbitMQQueueTest.php | 134 ++++++------------ 17 files changed, 130 insertions(+), 182 deletions(-) diff --git a/config/rabbitmq.php b/config/rabbitmq.php index 3e0000e5..7e3b8a06 100644 --- a/config/rabbitmq.php +++ b/config/rabbitmq.php @@ -3,7 +3,7 @@ /** * This is an example of queue connection configuration. * It will be merged into config/queue.php. - * You need to set proper values in `.env` + * You need to set proper values in `.env`. */ return [ diff --git a/src/LaravelQueueRabbitMQServiceProvider.php b/src/LaravelQueueRabbitMQServiceProvider.php index 9ce256ab..d3cc903e 100644 --- a/src/LaravelQueueRabbitMQServiceProvider.php +++ b/src/LaravelQueueRabbitMQServiceProvider.php @@ -16,7 +16,7 @@ class LaravelQueueRabbitMQServiceProvider extends ServiceProvider public function register() { $this->mergeConfigFrom( - __DIR__ . '/../config/rabbitmq.php', 'queue.connections.rabbitmq' + __DIR__.'/../config/rabbitmq.php', 'queue.connections.rabbitmq' ); } @@ -29,7 +29,7 @@ public function boot() { /** @var QueueManager $queue */ $queue = $this->app['queue']; - + $queue->addConnector('rabbitmq', function () { return new RabbitMQConnector($this->app['events']); }); diff --git a/src/Queue/Connectors/RabbitMQConnector.php b/src/Queue/Connectors/RabbitMQConnector.php index 8e8e0232..2017d22d 100644 --- a/src/Queue/Connectors/RabbitMQConnector.php +++ b/src/Queue/Connectors/RabbitMQConnector.php @@ -2,16 +2,16 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Connectors; +use Interop\Amqp\AmqpContext; +use Illuminate\Contracts\Queue\Queue; +use Interop\Amqp\AmqpConnectionFactory; use Enqueue\AmqpTools\DelayStrategyAware; -use Enqueue\AmqpTools\RabbitMqDlxDelayStrategy; use Illuminate\Contracts\Events\Dispatcher; -use Illuminate\Contracts\Queue\Queue; -use Illuminate\Queue\Connectors\ConnectorInterface; use Illuminate\Queue\Events\WorkerStopping; -use Interop\Amqp\AmqpConnectionFactory as InteropAmqpConnectionFactory; -use Interop\Amqp\AmqpConnectionFactory; -use Interop\Amqp\AmqpContext; +use Enqueue\AmqpTools\RabbitMqDlxDelayStrategy; +use Illuminate\Queue\Connectors\ConnectorInterface; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue; +use Interop\Amqp\AmqpConnectionFactory as InteropAmqpConnectionFactory; class RabbitMQConnector implements ConnectorInterface { diff --git a/src/Queue/Jobs/RabbitMQJob.php b/src/Queue/Jobs/RabbitMQJob.php index 12d15568..c5115b5d 100644 --- a/src/Queue/Jobs/RabbitMQJob.php +++ b/src/Queue/Jobs/RabbitMQJob.php @@ -3,14 +3,14 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Jobs; use Exception; -use Illuminate\Container\Container; -use Illuminate\Contracts\Queue\Job as JobContract; -use Illuminate\Database\DetectsDeadlocks; -use Illuminate\Queue\Jobs\Job; -use Illuminate\Queue\Jobs\JobName; use Illuminate\Support\Str; -use Interop\Amqp\AmqpConsumer; use Interop\Amqp\AmqpMessage; +use Illuminate\Queue\Jobs\Job; +use Interop\Amqp\AmqpConsumer; +use Illuminate\Queue\Jobs\JobName; +use Illuminate\Container\Container; +use Illuminate\Database\DetectsDeadlocks; +use Illuminate\Contracts\Queue\Job as JobContract; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue; class RabbitMQJob extends Job implements JobContract @@ -93,7 +93,7 @@ public function getRawBody(): string return $this->message->getBody(); } - /** @inheritdoc */ + /** {@inheritdoc} */ public function delete() { parent::delete(); @@ -101,7 +101,7 @@ public function delete() $this->consumer->acknowledge($this->message); } - /** @inheritdoc */ + /** {@inheritdoc} */ public function release($delay = 0) { parent::release($delay); @@ -158,7 +158,7 @@ public function setJobId($id) protected function unserialize(array $body) { try { - /** @noinspection UnserializeExploitsInspection */ + /* @noinspection UnserializeExploitsInspection */ return unserialize($body['data']['command']); } catch (Exception $exception) { if ( diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index dba78661..495074b0 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -2,15 +2,15 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Queue; -use Illuminate\Contracts\Queue\Queue as QueueContract; +use RuntimeException; use Illuminate\Queue\Queue; -use Interop\Amqp\AmqpContext; -use Interop\Amqp\AmqpMessage; use Interop\Amqp\AmqpQueue; use Interop\Amqp\AmqpTopic; -use Interop\Amqp\Impl\AmqpBind; use Psr\Log\LoggerInterface; -use RuntimeException; +use Interop\Amqp\AmqpContext; +use Interop\Amqp\AmqpMessage; +use Interop\Amqp\Impl\AmqpBind; +use Illuminate\Contracts\Queue\Queue as QueueContract; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Jobs\RabbitMQJob; class RabbitMQQueue extends Queue implements QueueContract @@ -46,7 +46,7 @@ public function __construct(AmqpContext $context, array $config) $this->sleepOnError = $config['sleep_on_error'] ?? 5; } - /** @inheritdoc */ + /** {@inheritdoc} */ public function size($queueName = null): int { /** @var AmqpQueue $queue */ @@ -55,18 +55,18 @@ public function size($queueName = null): int return $this->context->declareQueue($queue); } - /** @inheritdoc */ + /** {@inheritdoc} */ public function push($job, $data = '', $queue = null) { return $this->pushRaw($this->createPayload($job, $data), $queue, []); } - /** @inheritdoc */ + /** {@inheritdoc} */ public function pushRaw($payload, $queueName = null, array $options = []) { try { /** - * @var AmqpTopic $topic + * @var AmqpTopic * @var AmqpQueue $queue */ [$queue, $topic] = $this->declareEverything($queueName); @@ -100,11 +100,11 @@ public function pushRaw($payload, $queueName = null, array $options = []) } catch (\Exception $exception) { $this->reportConnectionError('pushRaw', $exception); - return null; + return; } } - /** @inheritdoc */ + /** {@inheritdoc} */ public function later($delay, $job, $data = '', $queue = null) { return $this->pushRaw($this->createPayload($job, $data), $queue, ['delay' => $this->secondsUntil($delay)]); @@ -124,11 +124,11 @@ public function release($delay, $job, $data, $queue, $attempts = 0) { return $this->pushRaw($this->createPayload($job, $data), $queue, [ 'delay' => $this->secondsUntil($delay), - 'attempts' => $attempts + 'attempts' => $attempts, ]); } - /** @inheritdoc */ + /** {@inheritdoc} */ public function pop($queueName = null) { try { @@ -143,8 +143,6 @@ public function pop($queueName = null) } catch (\Throwable $exception) { $this->reportConnectionError('pop', $exception); } - - return null; } /** @@ -200,7 +198,7 @@ protected function declareEverything(string $queueName = null): array $topic->addFlag(AmqpTopic::FLAG_AUTODELETE); } - if ($this->exchangeOptions['declare'] && !in_array($exchangeName, $this->declaredExchanges, true)) { + if ($this->exchangeOptions['declare'] && ! in_array($exchangeName, $this->declaredExchanges, true)) { $this->context->declareTopic($topic); $this->declaredExchanges[] = $exchangeName; @@ -221,7 +219,7 @@ protected function declareEverything(string $queueName = null): array $queue->addFlag(AmqpQueue::FLAG_AUTODELETE); } - if ($this->queueOptions['declare'] && !in_array($queueName, $this->declaredQueues, true)) { + if ($this->queueOptions['declare'] && ! in_array($queueName, $this->declaredQueues, true)) { $this->context->declareQueue($queue); $this->declaredQueues[] = $queueName; @@ -244,7 +242,7 @@ protected function reportConnectionError($action, \Throwable $e) /** @var LoggerInterface $logger */ $logger = $this->container['log']; - $logger->error('AMQP error while attempting ' . $action . ': ' . $e->getMessage()); + $logger->error('AMQP error while attempting '.$action.': '.$e->getMessage()); // If it's set to false, throw an error rather than waiting if ($this->sleepOnError === false) { diff --git a/tests/Functional/SendAndReceiveDelayedMessageTest.php b/tests/Functional/SendAndReceiveDelayedMessageTest.php index 03c33c0b..1a6ae946 100644 --- a/tests/Functional/SendAndReceiveDelayedMessageTest.php +++ b/tests/Functional/SendAndReceiveDelayedMessageTest.php @@ -2,15 +2,14 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Functional; -use Enqueue\AmqpLib\AmqpConnectionFactory; -use Illuminate\Container\Container; -use Illuminate\Events\Dispatcher; -use Interop\Amqp\AmqpTopic; -use PHPUnit\Framework\TestCase; use Psr\Log\NullLogger; -use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Connectors\RabbitMQConnector; -use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Jobs\RabbitMQJob; +use PHPUnit\Framework\TestCase; +use Illuminate\Events\Dispatcher; +use Illuminate\Container\Container; +use Enqueue\AmqpLib\AmqpConnectionFactory; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue; +use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Jobs\RabbitMQJob; +use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Connectors\RabbitMQConnector; /** * @group functional @@ -55,7 +54,7 @@ public function test() 'local_key' => null, 'verify_peer' => true, 'passphrase' => null, - ] + ], ]; $connector = new RabbitMQConnector(new Dispatcher()); diff --git a/tests/Functional/SendAndReceiveMessageTest.php b/tests/Functional/SendAndReceiveMessageTest.php index 562a9cc0..65cb7f54 100644 --- a/tests/Functional/SendAndReceiveMessageTest.php +++ b/tests/Functional/SendAndReceiveMessageTest.php @@ -2,15 +2,14 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Functional; -use Enqueue\AmqpLib\AmqpConnectionFactory; -use Illuminate\Container\Container; -use Illuminate\Events\Dispatcher; -use Interop\Amqp\AmqpTopic; -use PHPUnit\Framework\TestCase; use Psr\Log\NullLogger; -use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Connectors\RabbitMQConnector; -use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Jobs\RabbitMQJob; +use PHPUnit\Framework\TestCase; +use Illuminate\Events\Dispatcher; +use Illuminate\Container\Container; +use Enqueue\AmqpLib\AmqpConnectionFactory; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue; +use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Jobs\RabbitMQJob; +use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Connectors\RabbitMQConnector; /** * @group functional @@ -55,7 +54,7 @@ public function test() 'local_key' => null, 'verify_peer' => true, 'passphrase' => null, - ] + ], ]; $connector = new RabbitMQConnector(new Dispatcher()); diff --git a/tests/Functional/SslConnectionTest.php b/tests/Functional/SslConnectionTest.php index 87310f34..c9a44068 100644 --- a/tests/Functional/SslConnectionTest.php +++ b/tests/Functional/SslConnectionTest.php @@ -2,14 +2,13 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Functional; -use Enqueue\AmqpLib\AmqpConnectionFactory; +use PHPUnit\Framework\TestCase; use Enqueue\AmqpLib\AmqpContext; use Illuminate\Events\Dispatcher; -use Interop\Amqp\AmqpTopic; +use Enqueue\AmqpLib\AmqpConnectionFactory; use PhpAmqpLib\Connection\AMQPSSLConnection; -use PHPUnit\Framework\TestCase; -use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Connectors\RabbitMQConnector; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue; +use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Connectors\RabbitMQConnector; /** * @group functional @@ -54,7 +53,7 @@ public function testConnectorEstablishSecureConnectionWithRabbitMQBroker() 'local_key' => null, 'verify_peer' => false, 'passphrase' => null, - ] + ], ]; $connector = new RabbitMQConnector(new Dispatcher()); diff --git a/tests/Functional/StreamConnectionTest.php b/tests/Functional/StreamConnectionTest.php index b1558baf..74e55fb9 100644 --- a/tests/Functional/StreamConnectionTest.php +++ b/tests/Functional/StreamConnectionTest.php @@ -2,14 +2,13 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Functional; -use Enqueue\AmqpLib\AmqpConnectionFactory; +use PHPUnit\Framework\TestCase; use Enqueue\AmqpLib\AmqpContext; use Illuminate\Events\Dispatcher; -use Interop\Amqp\AmqpTopic; +use Enqueue\AmqpLib\AmqpConnectionFactory; use PhpAmqpLib\Connection\AMQPStreamConnection; -use PHPUnit\Framework\TestCase; -use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Connectors\RabbitMQConnector; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue; +use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Connectors\RabbitMQConnector; /** * @group functional @@ -54,7 +53,7 @@ public function testConnectorEstablishSecureConnectionWithRabbitMQBroker() 'local_key' => null, 'verify_peer' => true, 'passphrase' => null, - ] + ], ]; $connector = new RabbitMQConnector(new Dispatcher()); diff --git a/tests/LaravelQueueRabbitMQServiceProviderTest.php b/tests/LaravelQueueRabbitMQServiceProviderTest.php index c74a440f..de7eeba3 100644 --- a/tests/LaravelQueueRabbitMQServiceProviderTest.php +++ b/tests/LaravelQueueRabbitMQServiceProviderTest.php @@ -2,13 +2,13 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Tests; -use Illuminate\Container\Container; -use Illuminate\Contracts\Events\Dispatcher; +use PHPUnit\Framework\TestCase; use Illuminate\Queue\QueueManager; +use Illuminate\Container\Container; use Illuminate\Support\ServiceProvider; -use PHPUnit\Framework\TestCase; -use VladimirYuldashev\LaravelQueueRabbitMQ\LaravelQueueRabbitMQServiceProvider; +use Illuminate\Contracts\Events\Dispatcher; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Connectors\RabbitMQConnector; +use VladimirYuldashev\LaravelQueueRabbitMQ\LaravelQueueRabbitMQServiceProvider; class LaravelQueueRabbitMQServiceProviderTest extends TestCase { @@ -31,8 +31,7 @@ public function testShouldMergeQueueConfigOnRegister() $providerMock ->expects($this->once()) ->method('mergeConfigFrom') - ->with($dir.'/../config/rabbitmq.php', 'queue.connections.rabbitmq') - ; + ->with($dir.'/../config/rabbitmq.php', 'queue.connections.rabbitmq'); $providerMock->register(); } @@ -51,8 +50,7 @@ public function testShouldAddRabbitMQConnectorOnBoot() $this->assertInstanceOf(RabbitMQConnector::class, $connector); $this->assertAttributeSame($dispatcherMock, 'dispatcher', $connector); - }) - ; + }); $app = Container::getInstance(); $app['queue'] = $queueMock; diff --git a/tests/Mock/AmqpConnectionFactorySpy.php b/tests/Mock/AmqpConnectionFactorySpy.php index 5db9fdf8..a87986c8 100644 --- a/tests/Mock/AmqpConnectionFactorySpy.php +++ b/tests/Mock/AmqpConnectionFactorySpy.php @@ -1,4 +1,5 @@ createMock(AmqpContext::class); $contextMock ->expects($this->once()) - ->method('close') - ; + ->method('close'); $dispatcherMock = $this->createMock(Dispatcher::class); $dispatcherMock @@ -133,8 +132,7 @@ public function testShouldCallContextCloseMethodOnWorkerStoppingEvent() ->with(WorkerStopping::class, $this->isInstanceOf(\Closure::class)) ->willReturnCallback(function ($eventName, \Closure $listener) { $listener(); - }) - ; + }); CustomContextAmqpConnectionFactoryMock::$context = $contextMock; diff --git a/tests/Queue/Jobs/RabbitMQJobTest.php b/tests/Queue/Jobs/RabbitMQJobTest.php index 9f5695a4..6f4e67e7 100644 --- a/tests/Queue/Jobs/RabbitMQJobTest.php +++ b/tests/Queue/Jobs/RabbitMQJobTest.php @@ -2,16 +2,15 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Queue\Jobs; -use Illuminate\Container\Container; -use Illuminate\Contracts\Queue\Job as JobContract; -use Illuminate\Database\DetectsDeadlocks; use Illuminate\Queue\Jobs\Job; use Interop\Amqp\AmqpConsumer; -use Interop\Amqp\Impl\AmqpMessage; -use Interop\Amqp\Impl\AmqpQueue; use PHPUnit\Framework\TestCase; -use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Jobs\RabbitMQJob; +use Interop\Amqp\Impl\AmqpMessage; +use Illuminate\Container\Container; +use Illuminate\Database\DetectsDeadlocks; +use Illuminate\Contracts\Queue\Job as JobContract; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue; +use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Jobs\RabbitMQJob; class RabbitMQJobTest extends TestCase { @@ -42,23 +41,20 @@ public function testCouldBeConstructedWithExpectedArguments() $queue ->expects($this->once()) ->method('getQueueName') - ->willReturn('theQueueName') - ; + ->willReturn('theQueueName'); $consumerMock = $this->createConsumerMock(); $consumerMock ->expects($this->once()) ->method('getQueue') - ->willReturn($queue) - ; + ->willReturn($queue); $connectionMock = $this->createRabbitMQQueueMock(); $connectionMock ->expects($this->any()) ->method('getConnectionName') - ->willReturn('theConnectionName') - ; - + ->willReturn('theConnectionName'); + $job = new RabbitMQJob( new Container(), $connectionMock, diff --git a/tests/Queue/RabbitMQQueueTest.php b/tests/Queue/RabbitMQQueueTest.php index a07e79bd..ca9ebd8f 100644 --- a/tests/Queue/RabbitMQQueueTest.php +++ b/tests/Queue/RabbitMQQueueTest.php @@ -2,15 +2,15 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Queue; -use Illuminate\Container\Container; -use Interop\Amqp\AmqpConsumer; +use Interop\Amqp\AmqpQueue; +use Interop\Amqp\AmqpTopic; +use Psr\Log\LoggerInterface; use Interop\Amqp\AmqpContext; use Interop\Amqp\AmqpMessage; +use Interop\Amqp\AmqpConsumer; use Interop\Amqp\AmqpProducer; -use Interop\Amqp\AmqpQueue; -use Interop\Amqp\AmqpTopic; use PHPUnit\Framework\TestCase; -use Psr\Log\LoggerInterface; +use Illuminate\Container\Container; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Jobs\RabbitMQJob; class RabbitMQQueueTest extends TestCase @@ -77,20 +77,17 @@ public function testShouldReturnExpectedNumberOfMessages() $context ->expects($this->once()) ->method('createTopic') - ->willReturn($this->createMock(AmqpTopic::class)) - ; + ->willReturn($this->createMock(AmqpTopic::class)); $context ->expects($this->once()) ->method('createQueue') ->with($expectedQueueName) - ->willReturn($queue) - ; + ->willReturn($queue); $context ->expects($this->once()) ->method('declareQueue') ->with($this->identicalTo($queue)) - ->willReturn($expectedCount) - ; + ->willReturn($expectedCount); $queue = new RabbitMQQueue($context, $this->createDummyConfig()); $queue->setContainer($this->createDummyContainer()); @@ -120,37 +117,31 @@ public function testShouldSendExpectedMessageOnPushRaw() $this->assertSame(AmqpMessage::DELIVERY_MODE_PERSISTENT, $message->getDeliveryMode()); $this->assertNotEmpty($message->getCorrelationId()); $this->assertNull($message->getProperty(RabbitMQJob::ATTEMPT_COUNT_HEADERS_KEY)); - }) - ; + }); $producer ->expects($this->never()) - ->method('setDeliveryDelay') - ; + ->method('setDeliveryDelay'); $context = $this->createAmqpContext(); $context ->expects($this->once()) ->method('createTopic') - ->willReturn($topic) - ; + ->willReturn($topic); $context ->expects($this->once()) ->method('createMessage') ->with($expectedBody) - ->willReturn(new \Interop\Amqp\Impl\AmqpMessage($expectedBody)) - ; + ->willReturn(new \Interop\Amqp\Impl\AmqpMessage($expectedBody)); $context ->expects($this->once()) ->method('createQueue') ->with($expectedQueueName) - ->willReturn($queue) - ; + ->willReturn($queue); $context ->expects($this->once()) ->method('createProducer') - ->willReturn($producer) - ; + ->willReturn($producer); $queue = new RabbitMQQueue($context, $this->createDummyConfig()); $queue->setContainer($this->createDummyContainer()); @@ -171,35 +162,29 @@ public function testShouldSetAttemptCountPropIfNotNull() ->with($this->identicalTo($topic), $this->isInstanceOf(AmqpMessage::class)) ->willReturnCallback(function ($actualTopic, AmqpMessage $message) use ($expectedAttempts) { $this->assertSame($expectedAttempts, $message->getProperty(RabbitMQJob::ATTEMPT_COUNT_HEADERS_KEY)); - }) - ; + }); $producer ->expects($this->never()) - ->method('setDeliveryDelay') - ; + ->method('setDeliveryDelay'); $context = $this->createAmqpContext(); $context ->expects($this->once()) ->method('createTopic') - ->willReturn($topic) - ; + ->willReturn($topic); $context ->expects($this->once()) ->method('createMessage') ->with() - ->willReturn(new \Interop\Amqp\Impl\AmqpMessage()) - ; + ->willReturn(new \Interop\Amqp\Impl\AmqpMessage()); $context ->expects($this->once()) ->method('createQueue') - ->willReturn($this->createMock(AmqpQueue::class)) - ; + ->willReturn($this->createMock(AmqpQueue::class)); $context ->expects($this->once()) ->method('createProducer') - ->willReturn($producer) - ; + ->willReturn($producer); $queue = new RabbitMQQueue($context, $this->createDummyConfig()); $queue->setContainer($this->createDummyContainer()); @@ -217,36 +202,30 @@ public function testShouldSetDeliveryDelayIfDelayOptionPresent() $producer = $this->createMock(AmqpProducer::class); $producer ->expects($this->once()) - ->method('send') - ; + ->method('send'); $producer ->expects($this->once()) ->method('setDeliveryDelay') - ->with($expectedDeliveryDelay) - ; + ->with($expectedDeliveryDelay); $context = $this->createAmqpContext(); $context ->expects($this->once()) ->method('createTopic') - ->willReturn($topic) - ; + ->willReturn($topic); $context ->expects($this->once()) ->method('createMessage') ->with() - ->willReturn(new \Interop\Amqp\Impl\AmqpMessage()) - ; + ->willReturn(new \Interop\Amqp\Impl\AmqpMessage()); $context ->expects($this->once()) ->method('createQueue') - ->willReturn($this->createMock(AmqpQueue::class)) - ; + ->willReturn($this->createMock(AmqpQueue::class)); $context ->expects($this->once()) ->method('createProducer') - ->willReturn($producer) - ; + ->willReturn($producer); $queue = new RabbitMQQueue($context, $this->createDummyConfig()); $queue->setContainer($this->createDummyContainer()); @@ -262,42 +241,35 @@ public function testShouldLogExceptionOnPushRaw() ->method('send') ->willReturnCallback(function () { throw new \LogicException('Something went wrong while sending a message'); - }) - ; + }); $context = $this->createAmqpContext(); $context ->expects($this->once()) ->method('createTopic') - ->willReturn($this->createMock(AmqpTopic::class)) - ; + ->willReturn($this->createMock(AmqpTopic::class)); $context ->expects($this->once()) ->method('createMessage') - ->willReturn($this->createMock(AmqpMessage::class)) - ; + ->willReturn($this->createMock(AmqpMessage::class)); $context ->expects($this->once()) ->method('createQueue') - ->willReturn($this->createMock(AmqpQueue::class)) - ; + ->willReturn($this->createMock(AmqpQueue::class)); $context ->expects($this->once()) ->method('createProducer') - ->willReturn($producer) - ; + ->willReturn($producer); $logger = $this->createMock(LoggerInterface::class); $logger ->expects($this->once()) ->method('error') - ->with('AMQP error while attempting pushRaw: Something went wrong while sending a message') - ; + ->with('AMQP error while attempting pushRaw: Something went wrong while sending a message'); $container = new Container(); $container['log'] = $logger; - $queue = new RabbitMQQueue($context, $this->createDummyConfig()); $queue->setContainer($container); @@ -314,26 +286,22 @@ public function testShouldReturnNullIfNoMessagesOnQueue() $consumer ->expects($this->once()) ->method('receiveNoWait') - ->willReturn(null) - ; + ->willReturn(null); $context = $this->createAmqpContext(); $context ->expects($this->once()) ->method('createTopic') - ->willReturn($this->createMock(AmqpTopic::class)) - ; + ->willReturn($this->createMock(AmqpTopic::class)); $context ->expects($this->once()) ->method('createQueue') - ->willReturn($queue) - ; + ->willReturn($queue); $context ->expects($this->once()) ->method('createConsumer') ->with($this->identicalTo($queue)) - ->willReturn($consumer) - ; + ->willReturn($consumer); $queue = new RabbitMQQueue($context, $this->createDummyConfig()); $queue->setContainer($this->createDummyContainer()); @@ -351,31 +319,26 @@ public function testShouldReturnRabbitMQJobIfMessageReceivedFromQueue() $consumer ->expects($this->once()) ->method('receiveNoWait') - ->willReturn($message) - ; + ->willReturn($message); $consumer ->expects($this->once()) ->method('getQueue') - ->willReturn($queue) - ; + ->willReturn($queue); $context = $this->createAmqpContext(); $context ->expects($this->once()) ->method('createTopic') - ->willReturn($this->createMock(AmqpTopic::class)) - ; + ->willReturn($this->createMock(AmqpTopic::class)); $context ->expects($this->once()) ->method('createQueue') - ->willReturn($queue) - ; + ->willReturn($queue); $context ->expects($this->once()) ->method('createConsumer') ->with($this->identicalTo($queue)) - ->willReturn($consumer) - ; + ->willReturn($consumer); $queue = new RabbitMQQueue($context, $this->createDummyConfig()); $queue->setContainer($this->createDummyContainer()); @@ -395,33 +358,28 @@ public function testShouldLogExceptionOnPop() ->method('receiveNoWait') ->willReturnCallback(function () { throw new \LogicException('Something went wrong while receiving a message'); - }) - ; + }); $context = $this->createAmqpContext(); $context ->expects($this->once()) ->method('createTopic') - ->willReturn($this->createMock(AmqpTopic::class)) - ; + ->willReturn($this->createMock(AmqpTopic::class)); $context ->expects($this->once()) ->method('createQueue') - ->willReturn($queue) - ; + ->willReturn($queue); $context ->expects($this->once()) ->method('createConsumer') ->with($this->identicalTo($queue)) - ->willReturn($consumer) - ; + ->willReturn($consumer); $logger = $this->createMock(LoggerInterface::class); $logger ->expects($this->once()) ->method('error') - ->with('AMQP error while attempting pop: Something went wrong while receiving a message') - ; + ->with('AMQP error while attempting pop: Something went wrong while receiving a message'); $container = new Container(); $container['log'] = $logger; From 2014a757f1c87c72081c38ecd71400bf6d2aa351 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Mon, 10 Dec 2018 03:01:56 +0300 Subject: [PATCH 031/375] wip --- src/Queue/Jobs/RabbitMQJob.php | 17 ++++++++++------- src/Queue/RabbitMQQueue.php | 4 +++- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/Queue/Jobs/RabbitMQJob.php b/src/Queue/Jobs/RabbitMQJob.php index c5115b5d..98d11a44 100644 --- a/src/Queue/Jobs/RabbitMQJob.php +++ b/src/Queue/Jobs/RabbitMQJob.php @@ -20,7 +20,7 @@ class RabbitMQJob extends Job implements JobContract /** * Same as RabbitMQQueue, used for attempt counts. */ - const ATTEMPT_COUNT_HEADERS_KEY = 'attempts_count'; + public const ATTEMPT_COUNT_HEADERS_KEY = 'attempts_count'; protected $connection; protected $consumer; @@ -47,12 +47,12 @@ public function __construct( * * @return void */ - public function fire() + public function fire(): void { try { $payload = $this->payload(); - list($class, $method) = JobName::parse($payload['job']); + [$class, $method] = JobName::parse($payload['job']); with($this->instance = $this->resolve($class))->{$method}($this, $payload['data']); } catch (Exception $exception) { @@ -94,15 +94,17 @@ public function getRawBody(): string } /** {@inheritdoc} */ - public function delete() + public function delete(): void { parent::delete(); $this->consumer->acknowledge($this->message); } - /** {@inheritdoc} */ - public function release($delay = 0) + /** {@inheritdoc} + * @throws Exception + */ + public function release($delay = 0): void { parent::release($delay); @@ -128,6 +130,7 @@ public function release($delay = 0) * Get the job identifier. * * @return string + * @throws \Interop\Queue\Exception */ public function getJobId(): string { @@ -141,7 +144,7 @@ public function getJobId(): string * * @return void */ - public function setJobId($id) + public function setJobId($id): void { $this->connection->setCorrelationId($id); } diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index 495074b0..70116bb5 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -100,7 +100,7 @@ public function pushRaw($payload, $queueName = null, array $options = []) } catch (\Exception $exception) { $this->reportConnectionError('pushRaw', $exception); - return; + return null; } } @@ -142,6 +142,8 @@ public function pop($queueName = null) } } catch (\Throwable $exception) { $this->reportConnectionError('pop', $exception); + + return null; } } From 5898f6aa4e8cc1923eb7e9778ac384af059d62ec Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Mon, 10 Dec 2018 03:02:05 +0300 Subject: [PATCH 032/375] wip --- src/Queue/Connectors/RabbitMQConnector.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Queue/Connectors/RabbitMQConnector.php b/src/Queue/Connectors/RabbitMQConnector.php index 2017d22d..94ee1a1f 100644 --- a/src/Queue/Connectors/RabbitMQConnector.php +++ b/src/Queue/Connectors/RabbitMQConnector.php @@ -31,6 +31,7 @@ public function __construct(Dispatcher $dispatcher) * @param array $config * * @return Queue + * @throws \ReflectionException */ public function connect(array $config): Queue { From 7711ae6ab6be322eff9799c3539ed912cfff6e88 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Mon, 10 Dec 2018 00:02:33 +0000 Subject: [PATCH 033/375] Apply fixes from StyleCI --- src/Queue/RabbitMQQueue.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index 70116bb5..2e02e346 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -100,7 +100,7 @@ public function pushRaw($payload, $queueName = null, array $options = []) } catch (\Exception $exception) { $this->reportConnectionError('pushRaw', $exception); - return null; + return; } } @@ -143,7 +143,7 @@ public function pop($queueName = null) } catch (\Throwable $exception) { $this->reportConnectionError('pop', $exception); - return null; + return; } } From 9dec45c417ecfa5b0e2e482e832d26a9b3ba9807 Mon Sep 17 00:00:00 2001 From: Sergey Ivanenko Date: Mon, 10 Dec 2018 22:38:40 +0300 Subject: [PATCH 034/375] issue-206: fix createPayload for laravel 5.7.* --- src/Queue/RabbitMQQueue.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index 2e02e346..76fd52a9 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -58,7 +58,7 @@ public function size($queueName = null): int /** {@inheritdoc} */ public function push($job, $data = '', $queue = null) { - return $this->pushRaw($this->createPayload($job, $data), $queue, []); + return $this->pushRaw($this->createPayload($job, $queue, $data), $queue, []); } /** {@inheritdoc} */ @@ -107,7 +107,7 @@ public function pushRaw($payload, $queueName = null, array $options = []) /** {@inheritdoc} */ public function later($delay, $job, $data = '', $queue = null) { - return $this->pushRaw($this->createPayload($job, $data), $queue, ['delay' => $this->secondsUntil($delay)]); + return $this->pushRaw($this->createPayload($job, $queue, $data), $queue, ['delay' => $this->secondsUntil($delay)]); } /** @@ -122,7 +122,7 @@ public function later($delay, $job, $data = '', $queue = null) */ public function release($delay, $job, $data, $queue, $attempts = 0) { - return $this->pushRaw($this->createPayload($job, $data), $queue, [ + return $this->pushRaw($this->createPayload($job, $queue, $data), $queue, [ 'delay' => $this->secondsUntil($delay), 'attempts' => $attempts, ]); From 42762c76401511566d71e342a2a0477bcf90dab7 Mon Sep 17 00:00:00 2001 From: Nifras Ismail Date: Wed, 20 Feb 2019 18:10:46 +0530 Subject: [PATCH 035/375] Update README.md for lumen integration, we need to force the service provider changes. I also test this with my rabbitmq docker environment --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index 3a70d032..b114507e 100644 --- a/README.md +++ b/README.md @@ -120,6 +120,13 @@ Setup connection in `config/queue.php` ], ``` +## Lumen Usage + +For lumen usage the service provider should be registered manually as follow in bootstrap/app.php +``` +$app->register(VladimirYuldashev\LaravelQueueRabbitMQ\LaravelQueueRabbitMQServiceProvider::class); +``` + ## Usage Once you completed the configuration you can use Laravel Queue API. If you used other queue drivers you do not need to change anything else. If you do not know how to use Queue API, please refer to the official Laravel documentation: http://laravel.com/docs/queues From 6c644c16615f518cfc5a90da171656e23c48f2de Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Tue, 26 Feb 2019 00:10:27 +0300 Subject: [PATCH 036/375] switch to docker, add docker-compose --- .travis.yml | 21 ++++++++------------- composer.json | 6 +++--- docker-compose.yml | 21 +++++++++++++++++++++ phpunit.xml.dist | 2 +- tests/files/device.crt | 20 ++++++++++++++++++++ tests/files/device.csr | 17 +++++++++++++++++ tests/files/device.key | 27 +++++++++++++++++++++++++++ tests/files/rootCA.key | 27 +++++++++++++++++++++++++++ tests/files/rootCA.pem | 20 ++++++++++++++++++++ tests/files/rootCA.srl | 1 + 10 files changed, 145 insertions(+), 17 deletions(-) create mode 100644 docker-compose.yml create mode 100644 tests/files/device.crt create mode 100644 tests/files/device.csr create mode 100644 tests/files/device.key create mode 100644 tests/files/rootCA.key create mode 100644 tests/files/rootCA.pem create mode 100644 tests/files/rootCA.srl diff --git a/.travis.yml b/.travis.yml index b6eb16d2..aae53204 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,29 +6,24 @@ php: - 7.3 env: - - RABBITMQ_CONFIG_FILE="/tmp/rabbitmq.config" SSL_CAFILE="/tmp/rootCA.pem + - DOCKER_COMPOSE_VERSION=1.23.2 services: - - rabbitmq + - docker cache: directories: - $HOME/.composer/cache +before_install: + - sudo rm /usr/local/bin/docker-compose + - curl -L https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_VERSION}/docker-compose-`uname -s`-`uname -m` > docker-compose + - chmod +x docker-compose + - sudo mv docker-compose /usr/local/bin + before_script: - travis_retry composer self-update - travis_retry composer update --no-progress --no-interaction --prefer-dist - - sed -i 's///g' phpunit.xml.dist - - openssl genrsa -out /tmp/rootCA.key 2048 - - openssl req -x509 -new -nodes -key /tmp/rootCA.key -sha256 -days 1024 -out /tmp/rootCA.pem -subj "/C=US/ST=Arizona/L=Scottsdale/O=Example Company Inc./CN=127.0.0.1" - - openssl genrsa -out /tmp/device.key 2048 - - openssl req -new -key /tmp/device.key -out /tmp/device.csr -subj "/C=US/ST=Arizona/L=Scottsdale/O=Example Company Inc./CN=127.0.0.1" - - openssl x509 -req -in /tmp/device.csr -CA /tmp/rootCA.pem -CAkey /tmp/rootCA.key -CAcreateserial -out /tmp/device.crt -days 500 -sha256 - - sudo service rabbitmq-server stop - - echo "[{rabbit,[{ssl_listeners, [5671]},{ssl_options,[{cacertfile,\"/tmp/rootCA.pem\"},{certfile,\"/tmp/rootCA.pem\"},{keyfile,\"/tmp/rootCA.key\"},{verify,verify_none},{fail_if_no_peer_cert,false}]}]}]." > /tmp/rabbitmq.config - - cp /tmp/rabbitmq.config /tmp/rabbitmq.config.config - - sudo rabbitmq-server & - - sleep 10 script: - composer test diff --git a/composer.json b/composer.json index 4d955dc1..88e286da 100644 --- a/composer.json +++ b/composer.json @@ -11,9 +11,9 @@ "require": { "php": "^7.1.3", "ext-json": "*", - "illuminate/database": "5.6.*|5.7.*", - "illuminate/support": "5.6.*|5.7.*", - "illuminate/queue": "5.6.*|5.7.*", + "illuminate/database": "5.6.*|5.7.*|5.8.*", + "illuminate/support": "5.6.*|5.7.*|5.8.*", + "illuminate/queue": "5.6.*|5.7.*|5.8.*", "enqueue/amqp-lib": "0.8.*", "queue-interop/amqp-interop": "^0.7" }, diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..189e4ac8 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,21 @@ +version: '3.7' + +services: + + rabbitmq: + image: rabbitmq + environment: + RABBITMQ_DEFAULT_USER: guest + RABBITMQ_DEFAULT_PASSWORD: guest + RABBITMQ_DEFAULT_VHOST: / + RABBITMQ_SSL_CACERTFILE: /rootCA.pem + RABBITMQ_SSL_CERTFILE: /rootCA.pem + RABBITMQ_SSL_KEYFILE: /rootCA.key + RABBITMQ_SSL_VERIFY: verify_none + RABBITMQ_SSL_FAIL_IF_NO_PEER_CERT: "false" + volumes: + - "./tests/files/rootCA.pem:/rootCA.pem:ro" + - "./tests/files/rootCA.key:/rootCA.key:ro" + ports: + - 5671:5671 + - 5672:5672 diff --git a/phpunit.xml.dist b/phpunit.xml.dist index ef58e0a4..d31909dc 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -21,7 +21,7 @@ - + diff --git a/tests/files/device.crt b/tests/files/device.crt new file mode 100644 index 00000000..9589bcc4 --- /dev/null +++ b/tests/files/device.crt @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDSjCCAjICCQDZfnTLkt080jANBgkqhkiG9w0BAQsFADBnMQswCQYDVQQGEwJV +UzEQMA4GA1UECAwHQXJpem9uYTETMBEGA1UEBwwKU2NvdHRzZGFsZTEdMBsGA1UE +CgwURXhhbXBsZSBDb21wYW55IEluYy4xEjAQBgNVBAMMCTEyNy4wLjAuMTAeFw0x +OTAyMjUyMDU5MDRaFw0yMDA3MDkyMDU5MDRaMGcxCzAJBgNVBAYTAlVTMRAwDgYD +VQQIDAdBcml6b25hMRMwEQYDVQQHDApTY290dHNkYWxlMR0wGwYDVQQKDBRFeGFt +cGxlIENvbXBhbnkgSW5jLjESMBAGA1UEAwwJMTI3LjAuMC4xMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxaYllNmtkze8H/v6Vn0qIm16j+zujp605j1s +YCzUBKYxFPkoG7jzl7xC5k1P2DLHf7pJoUAPheBg19+0+lVijIU8f00OjQ5LRxGh +a6bpvhdR1yrQzw/ys8OOA7PIx2RRKB012GeTgKN93WDdWIJrr+EQMhMOob6562uv +GDOYu+/P/HU4WJGLBga0r8f49/iwwVpz30WLc6SpjKdTloAWsiAZ9ZU7Zb+HN2y+ +TpoXtwU15lFcKTfPVcvOi1iW/ypUiZX9e1KrpD66yptPnhQzijqkDHt2mUOJj8Nj +KR1URndCIAmpmglwghGpv9kIDpH9sMWxH8qUel33efIc6waigwIDAQABMA0GCSqG +SIb3DQEBCwUAA4IBAQCbPLDMc1dKlqfi75qxRpIaVD5rvIsaWiSwMYJJudWgUpbV +MS/w1RAkVRNpyC+qTFGivWTauNuGQyZOJBt7XLjXCQ7piALQKAXL4hvP+1wlRqlv +KQvi0rCeqBV21T8u1wkuF/yrdpDpxmDvLSxv/bKLTVYmESFMaez0rDtDSCbaBTeX +GqEJraVlfWJUE3PiscQA5gozPHxHfuiPSzBi1+4AaU/MSFc5hh45OapUs2lg4nmo +X6dyN2/rTmVBfUb87Ppl3z1IhjJMN7RqmmDdmJWRX5x0sRljpnIlA6WGmp6enDA3 +OMfP7ZZXqD/u5NnmAau6khcX8TAELkNrwBNC/fRJ +-----END CERTIFICATE----- diff --git a/tests/files/device.csr b/tests/files/device.csr new file mode 100644 index 00000000..b449d550 --- /dev/null +++ b/tests/files/device.csr @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIICrDCCAZQCAQAwZzELMAkGA1UEBhMCVVMxEDAOBgNVBAgMB0FyaXpvbmExEzAR +BgNVBAcMClNjb3R0c2RhbGUxHTAbBgNVBAoMFEV4YW1wbGUgQ29tcGFueSBJbmMu +MRIwEAYDVQQDDAkxMjcuMC4wLjEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQDFpiWU2a2TN7wf+/pWfSoibXqP7O6OnrTmPWxgLNQEpjEU+SgbuPOXvELm +TU/YMsd/ukmhQA+F4GDX37T6VWKMhTx/TQ6NDktHEaFrpum+F1HXKtDPD/Kzw44D +s8jHZFEoHTXYZ5OAo33dYN1Ygmuv4RAyEw6hvrnra68YM5i778/8dThYkYsGBrSv +x/j3+LDBWnPfRYtzpKmMp1OWgBayIBn1lTtlv4c3bL5Omhe3BTXmUVwpN89Vy86L +WJb/KlSJlf17UqukPrrKm0+eFDOKOqQMe3aZQ4mPw2MpHVRGd0IgCamaCXCCEam/ +2QgOkf2wxbEfypR6Xfd58hzrBqKDAgMBAAGgADANBgkqhkiG9w0BAQsFAAOCAQEA +X7NrRZ7gHhrxb97p41fwPvQGZbJdfotHcuTAq+zG06b9HyWMRQRp2aGCaPxz9Lrr +xJJex51O7zFE+F7rdQtQQhvB9NJjseStEHJIxhWyf45JVmI9e+TtljMrHTjiuMNZ +4cP5vR8Nf+PdYfRzGGWIS8W12XQ2gRy48QMmUBjwz6iE80byBIb2Upg3XEZvvJsy +28SeXvxV+IZr/gLWjLqW8CDJNCNp0shOKvvDzOda2nThorxvuZLhg0ykcaxFfr6R +yjzGAjFr++PZXAkwqkEeUz/1DN/1yQu5F6okaUfeOjkEFF96Zeez5KbXXF5kBCyw +XAF3lYeYmCOWGfzEYkVVHw== +-----END CERTIFICATE REQUEST----- diff --git a/tests/files/device.key b/tests/files/device.key new file mode 100644 index 00000000..e54a2074 --- /dev/null +++ b/tests/files/device.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEAxaYllNmtkze8H/v6Vn0qIm16j+zujp605j1sYCzUBKYxFPko +G7jzl7xC5k1P2DLHf7pJoUAPheBg19+0+lVijIU8f00OjQ5LRxGha6bpvhdR1yrQ +zw/ys8OOA7PIx2RRKB012GeTgKN93WDdWIJrr+EQMhMOob6562uvGDOYu+/P/HU4 +WJGLBga0r8f49/iwwVpz30WLc6SpjKdTloAWsiAZ9ZU7Zb+HN2y+TpoXtwU15lFc +KTfPVcvOi1iW/ypUiZX9e1KrpD66yptPnhQzijqkDHt2mUOJj8NjKR1URndCIAmp +mglwghGpv9kIDpH9sMWxH8qUel33efIc6waigwIDAQABAoIBAExlsFkc2s7w2DK0 +v0r3DnZIQvum5X8TMXFdhKqYKUuywX4N4Mb2cpHQHzvN3nL/DcX9R8Cgdl+VH1nS +Cq5ImtMeHQhHzLwRLl/GHNLzrZ3gfa3hytx+mZ2KlTYxJAaObCBJSirfvlAW4evU +KTqxDtbo882nOBylEFBDS2bbasoZdalEG/fosjXafyi0oObDO9gXlyLQRuQIPZPK +afTIB4LQM6K5o92y6qxJbFu+qPC46vzSeToOOeAUyOSA0XQ+9eNbPdFDPJnp5Pre +aL0bfr8Qmh+gufaA4UIANAlgVa7IiG5ccfxMg7nJxUWDR6H7MnQX7Zjuv/i7qP+P +dExyykECgYEA80w0PNpfRS0Eq87r2tKhtFJgHxAvC4m4Y8nhDoVSwYEaaJVYInnG +dHH9CyvV7TqAKWEkwBqJMo04AVQdOrOg2HJ+nsxrMa3RtUZFGpRYYbMVEX3o5VbL +sY3eQZhhgxzgcWkohHtDv/NhVksL97MW9KAGo+Qfi88Ma+SJTH/zyEkCgYEAz/fT +TUTURHzqs0Um4mgTvGj15H0ySyiMFuj5d+MdcYoy3HpW0htm0YlJISfo8bK1W4eq +bR7ltb3g9aqVFIh9szQ5mK91oDtSeYYUxBm9J7a75O/h+57tekcBAKUaarLYBpwR +7qmkGQcs1sILTdQIy4xqyu4sYP/EKvroT23KjGsCgYBhDH6x33GtSF3aormWGfsC +0PEisvPxKEhzFa4+epQeN/9uxFPZvLWa8XU8pYm6DWHeH6/nKS6dCZPTg9f8+HYq +oNE9StFfibRjGNqr1YzDvAmlZpImGU87ThngFIahJD2rP4U4A2ttAApNv7XQYpG/ +lq3PZknnHPoZd5oE9+0ocQKBgGNf9KQzg4rGdg/7tzzwpp2dOgJYoLOxSF+aK7rR +17vtYahg/SOg3Fy70Sn6vCDiWC7IgPNrlDBn7xr0zA/nuMjs56jCDt7l+d7/5uRd +uDlF5DrdNYrawndvflckjZ72nqtp4Fe+0B71gsOMLYKfEyTQkCcv8BzZmo8/Hcr2 +l5bJAoGAAzNnI8T2zdCZc5SAHYCrxwWQNX9F5Uwb15TMOHGU2xvd/G2DkvKIpv8f +NL0tX6KdRt5yVvV1YWqNhhRqjpocohsnzMqU5UHz2U6ejjr6ry2pQlGokTvnxyCL +526noBHZdTLPf09bcG/F7Sdq5/Q5GB+SQTW6mBYcwJIVG2oYxF0= +-----END RSA PRIVATE KEY----- diff --git a/tests/files/rootCA.key b/tests/files/rootCA.key new file mode 100644 index 00000000..483433b9 --- /dev/null +++ b/tests/files/rootCA.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpQIBAAKCAQEAw+xfX1g9xN1TGuRGD5xfpvIY8o8vGF8iWyBVd52YzCybnyPT +v5gmS7tndPkhfnL356gqJS/wHf7yD21YQhSzQZmOW1x9IxYsx4/mmmxpCYp9jzYo +/Rt2siNHB5ZH3Z8lwSGn6mnFQs40juIPS5dbngGDs5xDl0NkTO4jZRGiuS70OHHJ +NTRe9TsFs/12JggGVtIUmlPfpyflZBkhalD9mohoDw1egM0vLxaHMWYhIPEOVP4r +taMEYk2Y0wNDuOldqWMVzwBZFK5td8f4wqeYPBMfk9cK448XWYDZzShARnYTiGmc +evxgd+sFuEXQr/U7O/HUWTY3/YPFwSrx/ksL6QIDAQABAoIBAGPu6Sakx7zmd0E8 +NlA4HsH0sqzmQ8tWmxuH/pAonotmJWqix5rubHoseLS9bkwlMDXFHNoi/YMPS0B7 +MY1jKZvIS0hmgJ2o7eZMi/8wVNM9BJZLtdSEcaKjQ9Om37k3N/auyAtVL/zHWR+Y +RtzzsxOBCkBO8FrzUPG8delTeYCigIQM13rF3qsc5G7XEN5ypP0gz+m03EicVaXb +tMzzy4HwJq4T3ICj3TL6J5AzLHrUXl4g+DyakOfLCFqB1brWsWW12+2oYDeWOC6C +rzCsdSHGTWqSNC9GZwcgwy6PwVhykurTo9gFHBNLyxZHtKcKdlcpCrV/+oRRuZLT +6WY42AECgYEA+5F8a+w9K9dM2FebqXck3HupS3eku4FVRhreu8iZgCIvz+M1bb6N +bDtpjjz5N+CYYVp89esP0RjLCzcQUWkakb7Ek5QIA+srI/TmfGOZYFeQipgClAGr ++izoLRDSNEO7/RKzbWAh+tk9brzjhuIteegOtNRoXJSh/2ajHg+SgSkCgYEAx1/w +1ZVyJkYvfX+0qf64fvyodMC+mHsJ/UgtHM/gSi1b7b4obJqrHbnv+mBixDNh02dh +Jl7wlrwEQI4tk4cwZDh+dDwZ5F8px3/LqX+m2UfjS+rU30SwfZhdaa9qyUKBZBK3 +gutdWjDMa0C3Ng4dyu8b/onG14wzLs7VLKjzzMECgYEAzdzKUo6pqDyxd8CJc246 +TessKMOpnH9DxvCqIEURyBcxxQ8LY9kxZcZgpLMkxiMWz2P7KkrHULbXQUA4LEa0 +JVxVBOd4f6xsSypXiqb+liZR8/hc871CfKFPBcHkIjzjkz9AcVrfs6UeboZIMtLX +oBDUKApBtLE0uAnHpgvcObECgYEAsZDA7XgsMepQYXVbcgtqRa7AWTtQhH0QaIPf +qcl5+JZtSVASsKcPv2naUSOG0zbv6VgpLgNgQt8w6k22Sa4dayTleqAMb1hR3Vv0 +BwGpl9pulS6QaEjE5xbMG1Qfxx90HayNxAvbGHhdlygMBBiOcC6EwC306gPzkbyk +HyJaAAECgYEAjjcdQiQu3HhMidSMeZt8lBGSTLDIHujIY32+mOlBkIZsYiK2fnNZ +JZUPPmC9qDH+VmHyXZyKb2dxYUdiknptKUUu3NBrBP9LKqM78/yxFEdVSxiqlIBx +Dapv4fqsRreipdqFewo2YUvIgvowJj7p/EhlirX46XSDV7+zru3UBy0= +-----END RSA PRIVATE KEY----- diff --git a/tests/files/rootCA.pem b/tests/files/rootCA.pem new file mode 100644 index 00000000..094d8241 --- /dev/null +++ b/tests/files/rootCA.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDSjCCAjICCQCBIl41W59qTDANBgkqhkiG9w0BAQsFADBnMQswCQYDVQQGEwJV +UzEQMA4GA1UECAwHQXJpem9uYTETMBEGA1UEBwwKU2NvdHRzZGFsZTEdMBsGA1UE +CgwURXhhbXBsZSBDb21wYW55IEluYy4xEjAQBgNVBAMMCTEyNy4wLjAuMTAeFw0x +OTAyMjUyMDU3MjBaFw0yMTEyMTUyMDU3MjBaMGcxCzAJBgNVBAYTAlVTMRAwDgYD +VQQIDAdBcml6b25hMRMwEQYDVQQHDApTY290dHNkYWxlMR0wGwYDVQQKDBRFeGFt +cGxlIENvbXBhbnkgSW5jLjESMBAGA1UEAwwJMTI3LjAuMC4xMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw+xfX1g9xN1TGuRGD5xfpvIY8o8vGF8iWyBV +d52YzCybnyPTv5gmS7tndPkhfnL356gqJS/wHf7yD21YQhSzQZmOW1x9IxYsx4/m +mmxpCYp9jzYo/Rt2siNHB5ZH3Z8lwSGn6mnFQs40juIPS5dbngGDs5xDl0NkTO4j +ZRGiuS70OHHJNTRe9TsFs/12JggGVtIUmlPfpyflZBkhalD9mohoDw1egM0vLxaH +MWYhIPEOVP4rtaMEYk2Y0wNDuOldqWMVzwBZFK5td8f4wqeYPBMfk9cK448XWYDZ +zShARnYTiGmcevxgd+sFuEXQr/U7O/HUWTY3/YPFwSrx/ksL6QIDAQABMA0GCSqG +SIb3DQEBCwUAA4IBAQBT49Qz3kxu62oyk7xHvFinxrieF4oNqjivwHHHssEHsdb4 +N67YcMu5HHu78u035TH84jnsCjgKRgkzv9dSK5Pmqa9Qvt7rH3ziwdm9vr3Qg9NE +GIC09UrvNXmSfNirgIJAbmXyZvaGvLEUjenI7LNghdWsPZTNwwAwVSTiR7X6fmLe +Bci9wW7+oyjAJK+ct6mNPWe6s0x7TEJL1BhvfH1secFDF9dcq4UJ8/8sM3hbpmtb +MkdLpqm+rYBGazUCkiL1Rp9sRHDho03RORP9H0wOIyX6JPWHIeMZrXsQnEYhGFEM +6RDT11Rv4HdXJuIbVFVEgJK0v1Df8ZQS7I99Oh3H +-----END CERTIFICATE----- diff --git a/tests/files/rootCA.srl b/tests/files/rootCA.srl new file mode 100644 index 00000000..93842342 --- /dev/null +++ b/tests/files/rootCA.srl @@ -0,0 +1 @@ +D97E74CB92DD3CD2 From 0554455884dbd7ea836f9b88c6537f99a5807b7f Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Tue, 26 Feb 2019 00:13:13 +0300 Subject: [PATCH 037/375] switch to docker, add docker-compose --- .travis.yml | 1 + composer.json | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index aae53204..ccf065c6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,6 +24,7 @@ before_install: before_script: - travis_retry composer self-update - travis_retry composer update --no-progress --no-interaction --prefer-dist + - docker-compose up -d script: - composer test diff --git a/composer.json b/composer.json index 88e286da..bc5eec2b 100644 --- a/composer.json +++ b/composer.json @@ -18,8 +18,8 @@ "queue-interop/amqp-interop": "^0.7" }, "require-dev": { - "phpunit/phpunit": "^7.0", - "illuminate/events": "5.6.*|5.7.*", + "phpunit/phpunit": "^8.0", + "illuminate/events": "5.6.*|5.7.*|5.8.*", "mockery/mockery": "^1.0" }, "autoload": { From cca6e0ec96524dded285c53c96e66d152d338afc Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Tue, 26 Feb 2019 00:16:12 +0300 Subject: [PATCH 038/375] switch to docker, add docker-compose --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index bc5eec2b..d061aabb 100644 --- a/composer.json +++ b/composer.json @@ -18,7 +18,7 @@ "queue-interop/amqp-interop": "^0.7" }, "require-dev": { - "phpunit/phpunit": "^8.0", + "phpunit/phpunit": "^7.0", "illuminate/events": "5.6.*|5.7.*|5.8.*", "mockery/mockery": "^1.0" }, From 219066f599c3650ea2109eff15ae4c2788dcbd4a Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Tue, 26 Feb 2019 00:20:58 +0300 Subject: [PATCH 039/375] switch to docker, add docker-compose --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index ccf065c6..a0a5d0bf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,6 +25,7 @@ before_script: - travis_retry composer self-update - travis_retry composer update --no-progress --no-interaction --prefer-dist - docker-compose up -d + - sleep 10 script: - composer test From 5f7f3bb6d68d6742e501bad65e6f14f0dcfc74b5 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Tue, 26 Feb 2019 00:23:44 +0300 Subject: [PATCH 040/375] switch to docker, add docker-compose --- .travis.yml | 9 --------- 1 file changed, 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index a0a5d0bf..f33b4a20 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,9 +5,6 @@ php: - 7.2 - 7.3 -env: - - DOCKER_COMPOSE_VERSION=1.23.2 - services: - docker @@ -15,12 +12,6 @@ cache: directories: - $HOME/.composer/cache -before_install: - - sudo rm /usr/local/bin/docker-compose - - curl -L https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_VERSION}/docker-compose-`uname -s`-`uname -m` > docker-compose - - chmod +x docker-compose - - sudo mv docker-compose /usr/local/bin - before_script: - travis_retry composer self-update - travis_retry composer update --no-progress --no-interaction --prefer-dist From 423d41d17f9b41acccb56f8bbcb280fa5423df41 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Tue, 26 Feb 2019 00:24:03 +0300 Subject: [PATCH 041/375] switch to docker, add docker-compose --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index f33b4a20..8e9fb65f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,9 +13,9 @@ cache: - $HOME/.composer/cache before_script: + - docker-compose up -d - travis_retry composer self-update - travis_retry composer update --no-progress --no-interaction --prefer-dist - - docker-compose up -d - sleep 10 script: From 5542702ac94446ff581b2591237c347ed0d26ec7 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Tue, 26 Feb 2019 00:25:38 +0300 Subject: [PATCH 042/375] switch to docker, add docker-compose --- .travis.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.travis.yml b/.travis.yml index 8e9fb65f..77adb2fd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,9 @@ php: - 7.2 - 7.3 +env: + - DOCKER_COMPOSE_VERSION=1.23.2 + services: - docker @@ -12,6 +15,12 @@ cache: directories: - $HOME/.composer/cache +before_install: + - sudo rm /usr/local/bin/docker-compose + - curl -L https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_VERSION}/docker-compose-`uname -s`-`uname -m` > docker-compose + - chmod +x docker-compose + - sudo mv docker-compose /usr/local/bin + before_script: - docker-compose up -d - travis_retry composer self-update From e1f78e4dc54f3f81a2e0e4ca30185236617e0b9c Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Tue, 26 Feb 2019 22:08:47 +0300 Subject: [PATCH 043/375] laravel 5.8 --- tests/Queue/Connectors/RabbitMQConnectorTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Queue/Connectors/RabbitMQConnectorTest.php b/tests/Queue/Connectors/RabbitMQConnectorTest.php index 157325df..c5943093 100644 --- a/tests/Queue/Connectors/RabbitMQConnectorTest.php +++ b/tests/Queue/Connectors/RabbitMQConnectorTest.php @@ -185,7 +185,7 @@ private function createDummyConfig() 'arguments' => '[]', ], ], - 'sleep_on_error' => env('RABBITMQ_ERROR_SLEEP', 5), + 'sleep_on_error' => getenv('RABBITMQ_ERROR_SLEEP', 5), ]; } } From d6ae73077dddedb7f87c545a0ad4d4e41e45b69d Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Sun, 3 Mar 2019 00:57:13 +0300 Subject: [PATCH 044/375] wip --- composer.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index d061aabb..7f0e1180 100644 --- a/composer.json +++ b/composer.json @@ -20,7 +20,8 @@ "require-dev": { "phpunit/phpunit": "^7.0", "illuminate/events": "5.6.*|5.7.*|5.8.*", - "mockery/mockery": "^1.0" + "mockery/mockery": "^1.0", + "laravel/horizon": "^3.0" }, "autoload": { "psr-4": { From 22fada990cc1c596605580a771a65300fc1f9841 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Sun, 3 Mar 2019 01:05:42 +0300 Subject: [PATCH 045/375] update enqueue/amqp-lib to 0.9, queue-interop/amqp-interop to 0.8 --- composer.json | 4 +- tests/Mock/AmqpConnectionFactorySpy.php | 4 +- tests/Mock/AmqpContextMock.php | 50 +++++++++---------- ...CustomContextAmqpConnectionFactoryMock.php | 4 +- ...yStrategyAwareAmqpConnectionFactorySpy.php | 7 ++- 5 files changed, 36 insertions(+), 33 deletions(-) diff --git a/composer.json b/composer.json index d061aabb..3b2a4c0a 100644 --- a/composer.json +++ b/composer.json @@ -14,8 +14,8 @@ "illuminate/database": "5.6.*|5.7.*|5.8.*", "illuminate/support": "5.6.*|5.7.*|5.8.*", "illuminate/queue": "5.6.*|5.7.*|5.8.*", - "enqueue/amqp-lib": "0.8.*", - "queue-interop/amqp-interop": "^0.7" + "enqueue/amqp-lib": "0.9.*", + "queue-interop/amqp-interop": "0.8.*" }, "require-dev": { "phpunit/phpunit": "^7.0", diff --git a/tests/Mock/AmqpConnectionFactorySpy.php b/tests/Mock/AmqpConnectionFactorySpy.php index a87986c8..ced6b77b 100644 --- a/tests/Mock/AmqpConnectionFactorySpy.php +++ b/tests/Mock/AmqpConnectionFactorySpy.php @@ -2,6 +2,8 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Mock; +use Interop\Queue\Context; + class AmqpConnectionFactorySpy implements \Interop\Amqp\AmqpConnectionFactory { /** @var \Closure */ @@ -14,7 +16,7 @@ public function __construct($config) $spy($config); } - public function createContext() + public function createContext(): Context { return new AmqpContextMock(); } diff --git a/tests/Mock/AmqpContextMock.php b/tests/Mock/AmqpContextMock.php index e4605212..0d5564ea 100644 --- a/tests/Mock/AmqpContextMock.php +++ b/tests/Mock/AmqpContextMock.php @@ -6,97 +6,93 @@ use Interop\Amqp\AmqpQueue; use Interop\Amqp\AmqpTopic; use Interop\Amqp\AmqpContext; -use Interop\Amqp\AmqpConsumer; -use Interop\Queue\PsrDestination; +use Interop\Queue\Consumer; +use Interop\Queue\Destination; +use Interop\Queue\Message; +use Interop\Queue\Producer; +use Interop\Queue\Queue; +use Interop\Queue\SubscriptionConsumer; +use Interop\Queue\Topic; class AmqpContextMock implements AmqpContext { - public function declareTopic(AmqpTopic $topic) + public function createSubscriptionConsumer(): SubscriptionConsumer { throw new \LogicException('It is not expected to be called'); } - public function deleteTopic(AmqpTopic $topic) + public function declareTopic(AmqpTopic $topic): void { throw new \LogicException('It is not expected to be called'); } - public function declareQueue(AmqpQueue $queue) + public function deleteTopic(AmqpTopic $topic): void { throw new \LogicException('It is not expected to be called'); } - public function deleteQueue(AmqpQueue $queue) + public function declareQueue(AmqpQueue $queue): int { throw new \LogicException('It is not expected to be called'); } - public function purgeQueue(AmqpQueue $queue) + public function deleteQueue(AmqpQueue $queue): void { throw new \LogicException('It is not expected to be called'); } - public function bind(AmqpBind $bind) + public function purgeQueue(Queue $queue): void { throw new \LogicException('It is not expected to be called'); } - public function unbind(AmqpBind $bind) + public function bind(AmqpBind $bind): void { throw new \LogicException('It is not expected to be called'); } - public function setQos($prefetchSize, $prefetchCount, $global) + public function unbind(AmqpBind $bind): void { throw new \LogicException('It is not expected to be called'); } - public function subscribe(AmqpConsumer $consumer, callable $callback) + public function setQos(int $prefetchSize, int $prefetchCount, bool $global): void { throw new \LogicException('It is not expected to be called'); } - public function unsubscribe(AmqpConsumer $consumer) - { - throw new \LogicException('It is not expected to be called'); - } - - public function consume($timeout = 0) - { - throw new \LogicException('It is not expected to be called'); - } - public function close() + public function close(): void { throw new \LogicException('It is not expected to be called'); } - public function createQueue($queueName) + public function createQueue(string $queueName): Queue { throw new \LogicException('It is not expected to be called'); } - public function createTemporaryQueue() + public function createTemporaryQueue(): Queue { throw new \LogicException('It is not expected to be called'); } - public function createProducer() + public function createProducer(): Producer { throw new \LogicException('It is not expected to be called'); } - public function createConsumer(PsrDestination $destination) + public function createConsumer(Destination $destination): Consumer { throw new \LogicException('It is not expected to be called'); } - public function createTopic($amqpTopic) + public function createTopic(string $amqpTopic): Topic { throw new \LogicException('It is not expected to be called'); } - public function createMessage($body = '', array $properties = [], array $headers = []) + public function createMessage(string $body = '', array $properties = [], array $headers = []): Message { throw new \LogicException('It is not expected to be called'); } diff --git a/tests/Mock/CustomContextAmqpConnectionFactoryMock.php b/tests/Mock/CustomContextAmqpConnectionFactoryMock.php index e98aa6f7..11f28846 100644 --- a/tests/Mock/CustomContextAmqpConnectionFactoryMock.php +++ b/tests/Mock/CustomContextAmqpConnectionFactoryMock.php @@ -2,11 +2,13 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Mock; +use Interop\Queue\Context; + class CustomContextAmqpConnectionFactoryMock implements \Interop\Amqp\AmqpConnectionFactory { public static $context; - public function createContext() + public function createContext(): Context { return static::$context; } diff --git a/tests/Mock/DelayStrategyAwareAmqpConnectionFactorySpy.php b/tests/Mock/DelayStrategyAwareAmqpConnectionFactorySpy.php index acdea9a0..b7f702dd 100644 --- a/tests/Mock/DelayStrategyAwareAmqpConnectionFactorySpy.php +++ b/tests/Mock/DelayStrategyAwareAmqpConnectionFactorySpy.php @@ -4,13 +4,14 @@ use Enqueue\AmqpTools\DelayStrategy; use Enqueue\AmqpTools\DelayStrategyAware; +use Interop\Queue\Context; class DelayStrategyAwareAmqpConnectionFactorySpy implements \Interop\Amqp\AmqpConnectionFactory, DelayStrategyAware { /** @var \Closure */ public static $spy; - public function createContext() + public function createContext(): Context { return new AmqpContextMock(); } @@ -20,10 +21,12 @@ public function createContext() * * @return self */ - public function setDelayStrategy(DelayStrategy $delayStrategy = null) + public function setDelayStrategy(DelayStrategy $delayStrategy = null): DelayStrategyAware { $spy = static::$spy; $spy($delayStrategy); + + return $this; } } From a5f3f1e21b987e8e56ad15d3b472a5aaa7228c5c Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Sun, 3 Mar 2019 19:06:41 +0300 Subject: [PATCH 046/375] #225 laravel horizon support --- config/rabbitmq.php | 5 + src/Horizon/RabbitMQQueue.php | 115 ++++++++++++++++++ src/LaravelQueueRabbitMQServiceProvider.php | 4 +- src/Queue/Connectors/RabbitMQConnector.php | 12 +- src/Queue/Jobs/RabbitMQJob.php | 9 +- src/Queue/RabbitMQQueue.php | 18 +++ .../Functional/SendAndReceiveMessageTest.php | 28 +++-- 7 files changed, 175 insertions(+), 16 deletions(-) create mode 100644 src/Horizon/RabbitMQQueue.php diff --git a/config/rabbitmq.php b/config/rabbitmq.php index 7e3b8a06..6164806a 100644 --- a/config/rabbitmq.php +++ b/config/rabbitmq.php @@ -9,6 +9,11 @@ 'driver' => 'rabbitmq', + /* + * Set to horizon if you wish to use Laravel Horizon. + */ + 'worker' => env('RABBITMQ_WORKER', 'default'), + 'dsn' => env('RABBITMQ_DSN', null), /* diff --git a/src/Horizon/RabbitMQQueue.php b/src/Horizon/RabbitMQQueue.php new file mode 100644 index 00000000..36691725 --- /dev/null +++ b/src/Horizon/RabbitMQQueue.php @@ -0,0 +1,115 @@ +size($queue); + } + + /** {@inheritdoc} */ + public function push($job, $data = '', $queue = null) + { + $this->lastPushed = $job; + + return parent::push($job, $data, $queue); + } + + /** {@inheritdoc} */ + public function pushRaw($payload, $queueName = null, array $options = []) + { + $payload = (new JobPayload($payload))->prepare($this->lastPushed)->value; + + return tap(parent::pushRaw($payload, $queueName, $options), function () use ($queueName, $payload) { + $this->event($queueName ?: $this->queueName, new JobPushed($payload)); + }); + } + + /** {@inheritdoc} */ + public function later($delay, $job, $data = '', $queueName = null) + { + $payload = (new JobPayload($this->createPayload($job, $data)))->prepare($job)->value; + + return tap(parent::pushRaw($payload, $queueName, ['delay' => $this->secondsUntil($delay)]), function () use ($payload, $queueName) { + $this->event($queueName ?: $this->queueName, new JobPushed($payload)); + }); + } + + /** {@inheritdoc} */ + public function pop($queueName = null) + { + return tap(parent::pop($queueName), function ($result) use ($queueName) { + if ($result instanceof RabbitMQJob) { + $this->event($queueName ?: $this->queueName, new JobReserved($result->getRawBody())); + } + }); + } + + /** {@inheritdoc} */ + public function release($delay, $job, $data, $queue, $attempts = 0) + { + $this->lastPushed = $job; + + return parent::release($delay, $job, $data, $queue, $attempts); + } + + /** + * Fire the job deleted event. + * + * @param string $queueName + * @param RabbitMQJob $job + * @return void + * @throws \Illuminate\Contracts\Container\BindingResolutionException + */ + public function deleteReserved($queueName, $job): void + { + $this->event($queueName ?: $this->queueName, new JobDeleted($job, $job->getRawBody())); + } + + /** + * Fire the given event if a dispatcher is bound. + * + * @param string $queue + * @param mixed $event + * @return void + * @throws \Illuminate\Contracts\Container\BindingResolutionException + */ + protected function event($queue, $event): void + { + if ($this->container && $this->container->bound(Dispatcher::class)) { + $this->container->make(Dispatcher::class)->dispatch( + $event->connection($this->getConnectionName())->queue($queue) + ); + } + } + + /** {@inheritdoc} */ + protected function getRandomId(): string + { + return JobId::generate(); + } +} diff --git a/src/LaravelQueueRabbitMQServiceProvider.php b/src/LaravelQueueRabbitMQServiceProvider.php index d3cc903e..9d3d1184 100644 --- a/src/LaravelQueueRabbitMQServiceProvider.php +++ b/src/LaravelQueueRabbitMQServiceProvider.php @@ -13,7 +13,7 @@ class LaravelQueueRabbitMQServiceProvider extends ServiceProvider * * @return void */ - public function register() + public function register(): void { $this->mergeConfigFrom( __DIR__.'/../config/rabbitmq.php', 'queue.connections.rabbitmq' @@ -25,7 +25,7 @@ public function register() * * @return void */ - public function boot() + public function boot(): void { /** @var QueueManager $queue */ $queue = $this->app['queue']; diff --git a/src/Queue/Connectors/RabbitMQConnector.php b/src/Queue/Connectors/RabbitMQConnector.php index 94ee1a1f..80812288 100644 --- a/src/Queue/Connectors/RabbitMQConnector.php +++ b/src/Queue/Connectors/RabbitMQConnector.php @@ -2,6 +2,7 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Connectors; +use Illuminate\Support\Arr; use Interop\Amqp\AmqpContext; use Illuminate\Contracts\Queue\Queue; use Interop\Amqp\AmqpConnectionFactory; @@ -10,6 +11,7 @@ use Illuminate\Queue\Events\WorkerStopping; use Enqueue\AmqpTools\RabbitMqDlxDelayStrategy; use Illuminate\Queue\Connectors\ConnectorInterface; +use VladimirYuldashev\LaravelQueueRabbitMQ\Horizon\RabbitMQQueue as HorizonRabbitMQQueue; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue; use Interop\Amqp\AmqpConnectionFactory as InteropAmqpConnectionFactory; @@ -71,6 +73,14 @@ public function connect(array $config): Queue $context->close(); }); - return new RabbitMQQueue($context, $config); + $worker = Arr::get($config, 'worker', 'default'); + + if($worker === 'default') { + return new RabbitMQQueue($context, $config); + } + + if($worker === 'horizon') { + return new HorizonRabbitMQQueue($context, $config); + } } } diff --git a/src/Queue/Jobs/RabbitMQJob.php b/src/Queue/Jobs/RabbitMQJob.php index 98d11a44..05567abd 100644 --- a/src/Queue/Jobs/RabbitMQJob.php +++ b/src/Queue/Jobs/RabbitMQJob.php @@ -11,6 +11,7 @@ use Illuminate\Container\Container; use Illuminate\Database\DetectsDeadlocks; use Illuminate\Contracts\Queue\Job as JobContract; +use VladimirYuldashev\LaravelQueueRabbitMQ\Horizon\RabbitMQQueue as HorizonRabbitMQQueue; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue; class RabbitMQJob extends Job implements JobContract @@ -31,7 +32,8 @@ public function __construct( RabbitMQQueue $connection, AmqpConsumer $consumer, AmqpMessage $message - ) { + ) + { $this->container = $container; $this->connection = $connection; $this->consumer = $consumer; @@ -99,6 +101,11 @@ public function delete(): void parent::delete(); $this->consumer->acknowledge($this->message); + + // required for Laravel Horizon + if($this->connection instanceof HorizonRabbitMQQueue) { + $this->connection->deleteReserved($this->queue, $this); + } } /** {@inheritdoc} diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index 76fd52a9..9146988a 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -2,6 +2,7 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Queue; +use Illuminate\Support\Str; use RuntimeException; use Illuminate\Queue\Queue; use Interop\Amqp\AmqpQueue; @@ -254,4 +255,21 @@ protected function reportConnectionError($action, \Throwable $e) // Sleep so that we don't flood the log file sleep($this->sleepOnError); } + + protected function createPayloadArray($job, $queue, $data = '') + { + return array_merge(parent::createPayloadArray($job, $queue, $data), [ + 'id' => $this->getRandomId(), + ]); + } + + /** + * Get a random ID string. + * + * @return string + */ + protected function getRandomId(): string + { + return Str::random(32); + } } diff --git a/tests/Functional/SendAndReceiveMessageTest.php b/tests/Functional/SendAndReceiveMessageTest.php index 65cb7f54..6c63027b 100644 --- a/tests/Functional/SendAndReceiveMessageTest.php +++ b/tests/Functional/SendAndReceiveMessageTest.php @@ -20,12 +20,12 @@ public function test() { $config = [ 'factory_class' => AmqpConnectionFactory::class, - 'dsn' => null, - 'host' => getenv('HOST'), - 'port' => getenv('PORT'), - 'login' => 'guest', + 'dsn' => null, + 'host' => getenv('HOST'), + 'port' => getenv('PORT'), + 'login' => 'guest', 'password' => 'guest', - 'vhost' => '/', + 'vhost' => '/', 'options' => [ 'exchange' => [ 'name' => null, @@ -48,12 +48,12 @@ public function test() ], ], 'ssl_params' => [ - 'ssl_on' => false, - 'cafile' => null, - 'local_cert' => null, - 'local_key' => null, - 'verify_peer' => true, - 'passphrase' => null, + 'ssl_on' => false, + 'cafile' => null, + 'local_cert' => null, + 'local_key' => null, + 'verify_peer' => true, + 'passphrase' => null, ], ]; @@ -67,18 +67,22 @@ public function test() $queue->getContext()->purgeQueue($queue->getContext()->createQueue('default')); - $expectedPayload = __METHOD__.microtime(true); + $expectedPayload = __METHOD__ . microtime(true); $queue->pushRaw($expectedPayload); sleep(1); + $this->assertEquals(1, $queue->size()); + $job = $queue->pop(); $this->assertInstanceOf(RabbitMQJob::class, $job); $this->assertSame($expectedPayload, $job->getRawBody()); $job->delete(); + + $this->assertEquals(0, $queue->size()); } private function createDummyContainer() From 52c0285ee3db0992d6671e124ecb026c75f4d902 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Sun, 3 Mar 2019 16:10:12 +0000 Subject: [PATCH 047/375] Apply fixes from StyleCI --- tests/Mock/AmqpContextMock.php | 11 +++++------ .../DelayStrategyAwareAmqpConnectionFactorySpy.php | 2 +- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/tests/Mock/AmqpContextMock.php b/tests/Mock/AmqpContextMock.php index 0d5564ea..a1b392f9 100644 --- a/tests/Mock/AmqpContextMock.php +++ b/tests/Mock/AmqpContextMock.php @@ -2,17 +2,17 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Mock; +use Interop\Queue\Queue; +use Interop\Queue\Topic; use Interop\Amqp\AmqpBind; +use Interop\Queue\Message; use Interop\Amqp\AmqpQueue; use Interop\Amqp\AmqpTopic; -use Interop\Amqp\AmqpContext; use Interop\Queue\Consumer; -use Interop\Queue\Destination; -use Interop\Queue\Message; use Interop\Queue\Producer; -use Interop\Queue\Queue; +use Interop\Amqp\AmqpContext; +use Interop\Queue\Destination; use Interop\Queue\SubscriptionConsumer; -use Interop\Queue\Topic; class AmqpContextMock implements AmqpContext { @@ -61,7 +61,6 @@ public function setQos(int $prefetchSize, int $prefetchCount, bool $global): voi throw new \LogicException('It is not expected to be called'); } - public function close(): void { throw new \LogicException('It is not expected to be called'); diff --git a/tests/Mock/DelayStrategyAwareAmqpConnectionFactorySpy.php b/tests/Mock/DelayStrategyAwareAmqpConnectionFactorySpy.php index b7f702dd..933952c7 100644 --- a/tests/Mock/DelayStrategyAwareAmqpConnectionFactorySpy.php +++ b/tests/Mock/DelayStrategyAwareAmqpConnectionFactorySpy.php @@ -2,9 +2,9 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Mock; +use Interop\Queue\Context; use Enqueue\AmqpTools\DelayStrategy; use Enqueue\AmqpTools\DelayStrategyAware; -use Interop\Queue\Context; class DelayStrategyAwareAmqpConnectionFactorySpy implements \Interop\Amqp\AmqpConnectionFactory, DelayStrategyAware { From 43d38897b2c46429006d3511bf618704ceb9b912 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Sun, 3 Mar 2019 19:18:12 +0300 Subject: [PATCH 048/375] #225 laravel horizon support --- README.md | 30 +++++++++++++++++++++++------- config/rabbitmq.php | 2 +- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index b114507e..b8647364 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,11 @@ Setup connection in `config/queue.php` 'driver' => 'rabbitmq', + /* + * Set to "horizon" if you wish to use Laravel Horizon. + */ + 'worker' => env('RABBITMQ_WORKER', 'default'), + 'dsn' => env('RABBITMQ_DSN', null), /* @@ -120,16 +125,22 @@ Setup connection in `config/queue.php` ], ``` +## Laravel Usage + +Once you completed the configuration you can use Laravel Queue API. If you used other queue drivers you do not need to change anything else. If you do not know how to use Queue API, please refer to the official Laravel documentation: http://laravel.com/docs/queues + +## Laravel Horizon Usage + +This package supports [Laravel Horizon](http://horizon.laravel.com) out of the box. Firstly, install Horizon and then set `RABBITMQ_WORKER` to `horizon`. + ## Lumen Usage -For lumen usage the service provider should be registered manually as follow in bootstrap/app.php -``` +For Lumen usage the service provider should be registered manually as follow in `bootstrap/app.php`: + +```php $app->register(VladimirYuldashev\LaravelQueueRabbitMQ\LaravelQueueRabbitMQServiceProvider::class); ``` -## Usage - -Once you completed the configuration you can use Laravel Queue API. If you used other queue drivers you do not need to change anything else. If you do not know how to use Queue API, please refer to the official Laravel documentation: http://laravel.com/docs/queues ## Using other AMQP transports @@ -157,10 +168,15 @@ Change the factory class in `config/queue.php`: ## Testing -You can run the tests with: +Setup RabbitMQ using `docker-compose`: +```bash +docker-compose up -d +``` + +Run tests: ``` bash -vendor/bin/phpunit +composer test ``` ## Contribution diff --git a/config/rabbitmq.php b/config/rabbitmq.php index 6164806a..708d8bbc 100644 --- a/config/rabbitmq.php +++ b/config/rabbitmq.php @@ -10,7 +10,7 @@ 'driver' => 'rabbitmq', /* - * Set to horizon if you wish to use Laravel Horizon. + * Set to "horizon" if you wish to use Laravel Horizon. */ 'worker' => env('RABBITMQ_WORKER', 'default'), From c930937147f82a3af4d53c01f96c2e79c4fd755c Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Sun, 3 Mar 2019 19:22:04 +0300 Subject: [PATCH 049/375] #225 laravel horizon support --- src/Queue/Connectors/RabbitMQConnector.php | 31 ++++++++++++---------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/src/Queue/Connectors/RabbitMQConnector.php b/src/Queue/Connectors/RabbitMQConnector.php index 80812288..2cd081ed 100644 --- a/src/Queue/Connectors/RabbitMQConnector.php +++ b/src/Queue/Connectors/RabbitMQConnector.php @@ -11,6 +11,7 @@ use Illuminate\Queue\Events\WorkerStopping; use Enqueue\AmqpTools\RabbitMqDlxDelayStrategy; use Illuminate\Queue\Connectors\ConnectorInterface; +use InvalidArgumentException; use VladimirYuldashev\LaravelQueueRabbitMQ\Horizon\RabbitMQQueue as HorizonRabbitMQQueue; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue; use Interop\Amqp\AmqpConnectionFactory as InteropAmqpConnectionFactory; @@ -48,18 +49,18 @@ public function connect(array $config): Queue /** @var AmqpConnectionFactory $factory */ $factory = new $factoryClass([ - 'dsn' => $config['dsn'], - 'host' => $config['host'], - 'port' => $config['port'], - 'user' => $config['login'], - 'pass' => $config['password'], - 'vhost' => $config['vhost'], - 'ssl_on' => $config['ssl_params']['ssl_on'], - 'ssl_verify' => $config['ssl_params']['verify_peer'], - 'ssl_cacert' => $config['ssl_params']['cafile'], - 'ssl_cert' => $config['ssl_params']['local_cert'], - 'ssl_key' => $config['ssl_params']['local_key'], - 'ssl_passphrase' => $config['ssl_params']['passphrase'], + 'dsn' => Arr::get($config, 'dsn'), + 'host' => Arr::get($config, 'host', '127.0.0.1'), + 'port' => Arr::get($config, 'port', 5672), + 'user' => Arr::get($config, 'login', 'guest'), + 'pass' => Arr::get($config, 'password', 'guest'), + 'vhost' => Arr::get($config, 'vhost', '/'), + 'ssl_on' => Arr::get($config, 'ssl_params.ssl_on', false), + 'ssl_verify' => Arr::get($config, 'ssl_params.verify_peer', true), + 'ssl_cacert' => Arr::get($config, 'ssl_params.ca_file'), + 'ssl_cert' => Arr::get($config, 'ssl_params.local_cert'), + 'ssl_key' => Arr::get($config, 'ssl_params.local_key'), + 'ssl_passphrase' => Arr::get($config, 'ssl_params.passphrase'), ]); if ($factory instanceof DelayStrategyAware) { @@ -75,12 +76,14 @@ public function connect(array $config): Queue $worker = Arr::get($config, 'worker', 'default'); - if($worker === 'default') { + if ($worker === 'default') { return new RabbitMQQueue($context, $config); } - if($worker === 'horizon') { + if ($worker === 'horizon') { return new HorizonRabbitMQQueue($context, $config); } + + throw new InvalidArgumentException('Invalid worker.'); } } From 399b1902ddbb34c4a881e9af078c0f871aa18389 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Sun, 3 Mar 2019 19:23:24 +0300 Subject: [PATCH 050/375] #225 laravel horizon support --- src/Queue/Connectors/RabbitMQConnector.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Queue/Connectors/RabbitMQConnector.php b/src/Queue/Connectors/RabbitMQConnector.php index 2cd081ed..db3c390b 100644 --- a/src/Queue/Connectors/RabbitMQConnector.php +++ b/src/Queue/Connectors/RabbitMQConnector.php @@ -2,6 +2,7 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Connectors; +use Enqueue\AmqpLib\AmqpConnectionFactory as EnqueueAmqpConnectionFactory; use Illuminate\Support\Arr; use Interop\Amqp\AmqpContext; use Illuminate\Contracts\Queue\Queue; @@ -42,8 +43,9 @@ public function connect(array $config): Queue throw new \LogicException('The factory_class option is missing though it is required.'); } - $factoryClass = $config['factory_class']; - if (false === class_exists($factoryClass) || false === (new \ReflectionClass($factoryClass))->implementsInterface(InteropAmqpConnectionFactory::class)) { + $factoryClass = Arr::get($config, 'factory_class', EnqueueAmqpConnectionFactory::class); + + if (!class_exists($factoryClass) || !(new \ReflectionClass($factoryClass))->implementsInterface(InteropAmqpConnectionFactory::class)) { throw new \LogicException(sprintf('The factory_class option has to be valid class that implements "%s"', InteropAmqpConnectionFactory::class)); } From cd130d712070a9325b624ac264c93e0230abce21 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Sun, 3 Mar 2019 19:23:33 +0300 Subject: [PATCH 051/375] #225 laravel horizon support --- src/Queue/Connectors/RabbitMQConnector.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Queue/Connectors/RabbitMQConnector.php b/src/Queue/Connectors/RabbitMQConnector.php index db3c390b..5423b059 100644 --- a/src/Queue/Connectors/RabbitMQConnector.php +++ b/src/Queue/Connectors/RabbitMQConnector.php @@ -39,10 +39,6 @@ public function __construct(Dispatcher $dispatcher) */ public function connect(array $config): Queue { - if (false === array_key_exists('factory_class', $config)) { - throw new \LogicException('The factory_class option is missing though it is required.'); - } - $factoryClass = Arr::get($config, 'factory_class', EnqueueAmqpConnectionFactory::class); if (!class_exists($factoryClass) || !(new \ReflectionClass($factoryClass))->implementsInterface(InteropAmqpConnectionFactory::class)) { From 945ea313a82f4ada10c916fa929854922270c0c9 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Sun, 3 Mar 2019 19:33:45 +0300 Subject: [PATCH 052/375] #225 laravel horizon support --- src/Queue/Connectors/RabbitMQConnector.php | 2 +- tests/Queue/Connectors/RabbitMQConnectorTest.php | 9 --------- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/src/Queue/Connectors/RabbitMQConnector.php b/src/Queue/Connectors/RabbitMQConnector.php index 5423b059..d00128d2 100644 --- a/src/Queue/Connectors/RabbitMQConnector.php +++ b/src/Queue/Connectors/RabbitMQConnector.php @@ -55,7 +55,7 @@ public function connect(array $config): Queue 'vhost' => Arr::get($config, 'vhost', '/'), 'ssl_on' => Arr::get($config, 'ssl_params.ssl_on', false), 'ssl_verify' => Arr::get($config, 'ssl_params.verify_peer', true), - 'ssl_cacert' => Arr::get($config, 'ssl_params.ca_file'), + 'ssl_cacert' => Arr::get($config, 'ssl_params.cafile'), 'ssl_cert' => Arr::get($config, 'ssl_params.local_cert'), 'ssl_key' => Arr::get($config, 'ssl_params.local_key'), 'ssl_passphrase' => Arr::get($config, 'ssl_params.passphrase'), diff --git a/tests/Queue/Connectors/RabbitMQConnectorTest.php b/tests/Queue/Connectors/RabbitMQConnectorTest.php index c5943093..a2db6993 100644 --- a/tests/Queue/Connectors/RabbitMQConnectorTest.php +++ b/tests/Queue/Connectors/RabbitMQConnectorTest.php @@ -28,15 +28,6 @@ public function testCouldBeConstructedWithDispatcherAsFirstArgument() new RabbitMQConnector($this->createMock(Dispatcher::class)); } - public function testThrowsIfFactoryClassIsMissing() - { - $connector = new RabbitMQConnector($this->createMock(Dispatcher::class)); - - $this->expectException(\LogicException::class); - $this->expectExceptionMessage('The factory_class option is missing though it is required.'); - $connector->connect([]); - } - public function testThrowsIfFactoryClassIsNotValidClass() { $connector = new RabbitMQConnector($this->createMock(Dispatcher::class)); From 9388ae9ecce86d59627c7b6df180fe770c9dfaab Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Mon, 4 Mar 2019 23:32:40 +0300 Subject: [PATCH 053/375] #225 laravel horizon support --- composer.json | 2 +- src/Horizon/RabbitMQQueue.php | 8 +++---- src/Queue/RabbitMQQueue.php | 40 +++++++++++++++++++---------------- 3 files changed, 27 insertions(+), 23 deletions(-) diff --git a/composer.json b/composer.json index 9f5c9cb1..026ec4a2 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "vladimir-yuldashev/laravel-queue-rabbitmq", - "description": "RabbitMQ driver for Laravel Queue", + "description": "RabbitMQ driver for Laravel Queue. Supports Laravel Horizon", "license": "MIT", "authors": [ { diff --git a/src/Horizon/RabbitMQQueue.php b/src/Horizon/RabbitMQQueue.php index 36691725..5c5659cc 100644 --- a/src/Horizon/RabbitMQQueue.php +++ b/src/Horizon/RabbitMQQueue.php @@ -23,7 +23,7 @@ class RabbitMQQueue extends BaseRabbitMQQueue /** * Get the number of queue jobs that are ready to process. * - * @param string|null $queue + * @param string|null $queue * @return int */ public function readyNow($queue = null): int @@ -45,7 +45,7 @@ public function pushRaw($payload, $queueName = null, array $options = []) $payload = (new JobPayload($payload))->prepare($this->lastPushed)->value; return tap(parent::pushRaw($payload, $queueName, $options), function () use ($queueName, $payload) { - $this->event($queueName ?: $this->queueName, new JobPushed($payload)); + $this->event($this->getQueueName($queueName), new JobPushed($payload)); }); } @@ -55,7 +55,7 @@ public function later($delay, $job, $data = '', $queueName = null) $payload = (new JobPayload($this->createPayload($job, $data)))->prepare($job)->value; return tap(parent::pushRaw($payload, $queueName, ['delay' => $this->secondsUntil($delay)]), function () use ($payload, $queueName) { - $this->event($queueName ?: $this->queueName, new JobPushed($payload)); + $this->event($this->getQueueName($queueName), new JobPushed($payload)); }); } @@ -87,7 +87,7 @@ public function release($delay, $job, $data, $queue, $attempts = 0) */ public function deleteReserved($queueName, $job): void { - $this->event($queueName ?: $this->queueName, new JobDeleted($job, $job->getRawBody())); + $this->event($this->getQueueName($queueName), new JobDeleted($job, $job->getRawBody())); } /** diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index 9146988a..aaf4cfc6 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -185,7 +185,7 @@ public function getContext(): AmqpContext */ protected function declareEverything(string $queueName = null): array { - $queueName = $queueName ?: $this->queueName; + $queueName = $this->getQueueName($queueName); $exchangeName = $this->exchangeOptions['name'] ?: $queueName; $topic = $this->context->createTopic($exchangeName); @@ -235,6 +235,27 @@ protected function declareEverything(string $queueName = null): array return [$queue, $topic]; } + protected function getQueueName($queueName = null) { + return $queueName ?: $this->queueName; + } + + protected function createPayloadArray($job, $queue, $data = '') + { + return array_merge(parent::createPayloadArray($job, $queue, $data), [ + 'id' => $this->getRandomId(), + ]); + } + + /** + * Get a random ID string. + * + * @return string + */ + protected function getRandomId(): string + { + return Str::random(32); + } + /** * @param string $action * @param \Throwable $e @@ -255,21 +276,4 @@ protected function reportConnectionError($action, \Throwable $e) // Sleep so that we don't flood the log file sleep($this->sleepOnError); } - - protected function createPayloadArray($job, $queue, $data = '') - { - return array_merge(parent::createPayloadArray($job, $queue, $data), [ - 'id' => $this->getRandomId(), - ]); - } - - /** - * Get a random ID string. - * - * @return string - */ - protected function getRandomId(): string - { - return Str::random(32); - } } From 34098ce33e11d2445b34af6c3eaf569ed9a9e3e1 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Mon, 4 Mar 2019 23:33:17 +0300 Subject: [PATCH 054/375] #225 laravel horizon support --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b8647364..4b07bebe 100644 --- a/README.md +++ b/README.md @@ -131,7 +131,7 @@ Once you completed the configuration you can use Laravel Queue API. If you used ## Laravel Horizon Usage -This package supports [Laravel Horizon](http://horizon.laravel.com) out of the box. Firstly, install Horizon and then set `RABBITMQ_WORKER` to `horizon`. +Starting with 7.4, this package supports [Laravel Horizon](http://horizon.laravel.com) out of the box. Firstly, install Horizon and then set `RABBITMQ_WORKER` to `horizon`. ## Lumen Usage From 35815d823074b97e895e34606dc0b51085504700 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Mon, 4 Mar 2019 20:38:14 +0000 Subject: [PATCH 055/375] Apply fixes from StyleCI --- src/Horizon/RabbitMQQueue.php | 8 ++++---- src/Queue/Connectors/RabbitMQConnector.php | 8 ++++---- src/Queue/Jobs/RabbitMQJob.php | 7 +++---- src/Queue/RabbitMQQueue.php | 5 +++-- tests/Functional/SendAndReceiveMessageTest.php | 2 +- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/Horizon/RabbitMQQueue.php b/src/Horizon/RabbitMQQueue.php index 5c5659cc..69ef4060 100644 --- a/src/Horizon/RabbitMQQueue.php +++ b/src/Horizon/RabbitMQQueue.php @@ -2,12 +2,12 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Horizon; -use Illuminate\Contracts\Events\Dispatcher; -use Laravel\Horizon\Events\JobDeleted; -use Laravel\Horizon\Events\JobPushed; -use Laravel\Horizon\Events\JobReserved; use Laravel\Horizon\JobId; use Laravel\Horizon\JobPayload; +use Laravel\Horizon\Events\JobPushed; +use Laravel\Horizon\Events\JobDeleted; +use Laravel\Horizon\Events\JobReserved; +use Illuminate\Contracts\Events\Dispatcher; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Jobs\RabbitMQJob; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue as BaseRabbitMQQueue; diff --git a/src/Queue/Connectors/RabbitMQConnector.php b/src/Queue/Connectors/RabbitMQConnector.php index d00128d2..bc7250d4 100644 --- a/src/Queue/Connectors/RabbitMQConnector.php +++ b/src/Queue/Connectors/RabbitMQConnector.php @@ -2,9 +2,9 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Connectors; -use Enqueue\AmqpLib\AmqpConnectionFactory as EnqueueAmqpConnectionFactory; use Illuminate\Support\Arr; use Interop\Amqp\AmqpContext; +use InvalidArgumentException; use Illuminate\Contracts\Queue\Queue; use Interop\Amqp\AmqpConnectionFactory; use Enqueue\AmqpTools\DelayStrategyAware; @@ -12,10 +12,10 @@ use Illuminate\Queue\Events\WorkerStopping; use Enqueue\AmqpTools\RabbitMqDlxDelayStrategy; use Illuminate\Queue\Connectors\ConnectorInterface; -use InvalidArgumentException; -use VladimirYuldashev\LaravelQueueRabbitMQ\Horizon\RabbitMQQueue as HorizonRabbitMQQueue; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue; use Interop\Amqp\AmqpConnectionFactory as InteropAmqpConnectionFactory; +use Enqueue\AmqpLib\AmqpConnectionFactory as EnqueueAmqpConnectionFactory; +use VladimirYuldashev\LaravelQueueRabbitMQ\Horizon\RabbitMQQueue as HorizonRabbitMQQueue; class RabbitMQConnector implements ConnectorInterface { @@ -41,7 +41,7 @@ public function connect(array $config): Queue { $factoryClass = Arr::get($config, 'factory_class', EnqueueAmqpConnectionFactory::class); - if (!class_exists($factoryClass) || !(new \ReflectionClass($factoryClass))->implementsInterface(InteropAmqpConnectionFactory::class)) { + if (! class_exists($factoryClass) || ! (new \ReflectionClass($factoryClass))->implementsInterface(InteropAmqpConnectionFactory::class)) { throw new \LogicException(sprintf('The factory_class option has to be valid class that implements "%s"', InteropAmqpConnectionFactory::class)); } diff --git a/src/Queue/Jobs/RabbitMQJob.php b/src/Queue/Jobs/RabbitMQJob.php index 05567abd..dbcf7b14 100644 --- a/src/Queue/Jobs/RabbitMQJob.php +++ b/src/Queue/Jobs/RabbitMQJob.php @@ -11,8 +11,8 @@ use Illuminate\Container\Container; use Illuminate\Database\DetectsDeadlocks; use Illuminate\Contracts\Queue\Job as JobContract; -use VladimirYuldashev\LaravelQueueRabbitMQ\Horizon\RabbitMQQueue as HorizonRabbitMQQueue; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue; +use VladimirYuldashev\LaravelQueueRabbitMQ\Horizon\RabbitMQQueue as HorizonRabbitMQQueue; class RabbitMQJob extends Job implements JobContract { @@ -32,8 +32,7 @@ public function __construct( RabbitMQQueue $connection, AmqpConsumer $consumer, AmqpMessage $message - ) - { + ) { $this->container = $container; $this->connection = $connection; $this->consumer = $consumer; @@ -103,7 +102,7 @@ public function delete(): void $this->consumer->acknowledge($this->message); // required for Laravel Horizon - if($this->connection instanceof HorizonRabbitMQQueue) { + if ($this->connection instanceof HorizonRabbitMQQueue) { $this->connection->deleteReserved($this->queue, $this); } } diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index aaf4cfc6..bdbda96c 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -2,9 +2,9 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Queue; -use Illuminate\Support\Str; use RuntimeException; use Illuminate\Queue\Queue; +use Illuminate\Support\Str; use Interop\Amqp\AmqpQueue; use Interop\Amqp\AmqpTopic; use Psr\Log\LoggerInterface; @@ -235,7 +235,8 @@ protected function declareEverything(string $queueName = null): array return [$queue, $topic]; } - protected function getQueueName($queueName = null) { + protected function getQueueName($queueName = null) + { return $queueName ?: $this->queueName; } diff --git a/tests/Functional/SendAndReceiveMessageTest.php b/tests/Functional/SendAndReceiveMessageTest.php index 6c63027b..b5aa6a77 100644 --- a/tests/Functional/SendAndReceiveMessageTest.php +++ b/tests/Functional/SendAndReceiveMessageTest.php @@ -67,7 +67,7 @@ public function test() $queue->getContext()->purgeQueue($queue->getContext()->createQueue('default')); - $expectedPayload = __METHOD__ . microtime(true); + $expectedPayload = __METHOD__.microtime(true); $queue->pushRaw($expectedPayload); From 19fc898752b4a1e8c8a84afa9b5d11506d4f2e6b Mon Sep 17 00:00:00 2001 From: Emiel Bom Date: Sun, 17 Mar 2019 15:52:03 +0100 Subject: [PATCH 056/375] Added extra listener when horizon is used as worker. --- src/Horizon/Listeners/RabbitMQFailedEvent.php | 46 +++++++++++++++++++ src/Queue/Connectors/RabbitMQConnector.php | 3 ++ 2 files changed, 49 insertions(+) create mode 100644 src/Horizon/Listeners/RabbitMQFailedEvent.php diff --git a/src/Horizon/Listeners/RabbitMQFailedEvent.php b/src/Horizon/Listeners/RabbitMQFailedEvent.php new file mode 100644 index 00000000..c9fcfc35 --- /dev/null +++ b/src/Horizon/Listeners/RabbitMQFailedEvent.php @@ -0,0 +1,46 @@ +events = $events; + } + + /** + * Handle the event. + * + * @param \Illuminate\Queue\Events\JobFailed $event + * @return void + */ + public function handle(LaravelJobFailed $event) + { + if (!$event->job instanceof RabbitMQJob) { + return; + } + + $this->events->dispatch((new HorizonJobFailed( + $event->exception, $event->job, $event->job->getRawBody() + ))->connection($event->connectionName)->queue($event->job->getQueue())); + } +} diff --git a/src/Queue/Connectors/RabbitMQConnector.php b/src/Queue/Connectors/RabbitMQConnector.php index bc7250d4..56aa98ef 100644 --- a/src/Queue/Connectors/RabbitMQConnector.php +++ b/src/Queue/Connectors/RabbitMQConnector.php @@ -16,6 +16,8 @@ use Interop\Amqp\AmqpConnectionFactory as InteropAmqpConnectionFactory; use Enqueue\AmqpLib\AmqpConnectionFactory as EnqueueAmqpConnectionFactory; use VladimirYuldashev\LaravelQueueRabbitMQ\Horizon\RabbitMQQueue as HorizonRabbitMQQueue; +use VladimirYuldashev\LaravelQueueRabbitMQ\Horizon\Listeners\RabbitMQFailedEvent; +use Illuminate\Queue\Events\JobFailed; class RabbitMQConnector implements ConnectorInterface { @@ -79,6 +81,7 @@ public function connect(array $config): Queue } if ($worker === 'horizon') { + $this->dispatcher->listen(JobFailed::class, RabbitMQFailedEvent::class); return new HorizonRabbitMQQueue($context, $config); } From b6a5ab6fcc78d47ab2b25a3f9b30050232dd3ca9 Mon Sep 17 00:00:00 2001 From: Emiel Bom Date: Sun, 17 Mar 2019 16:14:43 +0100 Subject: [PATCH 057/375] try to fix style issues --- src/Horizon/Listeners/RabbitMQFailedEvent.php | 4 ++-- src/Queue/Connectors/RabbitMQConnector.php | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Horizon/Listeners/RabbitMQFailedEvent.php b/src/Horizon/Listeners/RabbitMQFailedEvent.php index c9fcfc35..e13445f4 100644 --- a/src/Horizon/Listeners/RabbitMQFailedEvent.php +++ b/src/Horizon/Listeners/RabbitMQFailedEvent.php @@ -3,8 +3,8 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Horizon\Listeners; use Illuminate\Contracts\Events\Dispatcher; -use Illuminate\Queue\Events\JobFailed as LaravelJobFailed; use Laravel\Horizon\Events\JobFailed as HorizonJobFailed; +use Illuminate\Queue\Events\JobFailed as LaravelJobFailed; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Jobs\RabbitMQJob; class RabbitMQFailedEvent @@ -35,7 +35,7 @@ public function __construct(Dispatcher $events) */ public function handle(LaravelJobFailed $event) { - if (!$event->job instanceof RabbitMQJob) { + if (! $event->job instanceof RabbitMQJob) { return; } diff --git a/src/Queue/Connectors/RabbitMQConnector.php b/src/Queue/Connectors/RabbitMQConnector.php index 56aa98ef..59820fa7 100644 --- a/src/Queue/Connectors/RabbitMQConnector.php +++ b/src/Queue/Connectors/RabbitMQConnector.php @@ -6,6 +6,7 @@ use Interop\Amqp\AmqpContext; use InvalidArgumentException; use Illuminate\Contracts\Queue\Queue; +use Illuminate\Queue\Events\JobFailed; use Interop\Amqp\AmqpConnectionFactory; use Enqueue\AmqpTools\DelayStrategyAware; use Illuminate\Contracts\Events\Dispatcher; @@ -15,9 +16,8 @@ use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue; use Interop\Amqp\AmqpConnectionFactory as InteropAmqpConnectionFactory; use Enqueue\AmqpLib\AmqpConnectionFactory as EnqueueAmqpConnectionFactory; -use VladimirYuldashev\LaravelQueueRabbitMQ\Horizon\RabbitMQQueue as HorizonRabbitMQQueue; use VladimirYuldashev\LaravelQueueRabbitMQ\Horizon\Listeners\RabbitMQFailedEvent; -use Illuminate\Queue\Events\JobFailed; +use VladimirYuldashev\LaravelQueueRabbitMQ\Horizon\RabbitMQQueue as HorizonRabbitMQQueue; class RabbitMQConnector implements ConnectorInterface { @@ -82,6 +82,7 @@ public function connect(array $config): Queue if ($worker === 'horizon') { $this->dispatcher->listen(JobFailed::class, RabbitMQFailedEvent::class); + return new HorizonRabbitMQQueue($context, $config); } From 3ee048910abb54b7fd730ea328ddb1db8161a563 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Mon, 25 Mar 2019 13:54:44 +0300 Subject: [PATCH 058/375] hotfix #229 by removing createPayloadArray since it changed in Laravel 5.7 --- src/Queue/RabbitMQQueue.php | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index bdbda96c..844aad16 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -240,13 +240,6 @@ protected function getQueueName($queueName = null) return $queueName ?: $this->queueName; } - protected function createPayloadArray($job, $queue, $data = '') - { - return array_merge(parent::createPayloadArray($job, $queue, $data), [ - 'id' => $this->getRandomId(), - ]); - } - /** * Get a random ID string. * From 8e38e502c36b3ee176f524a3e9532624a19892de Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Wed, 27 Mar 2019 22:33:29 +0300 Subject: [PATCH 059/375] fix horizon and lowest required laravel version is 5.7 --- composer.json | 10 +++++----- src/Queue/RabbitMQQueue.php | 7 +++++++ 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/composer.json b/composer.json index 026ec4a2..eec27d09 100644 --- a/composer.json +++ b/composer.json @@ -11,15 +11,15 @@ "require": { "php": "^7.1.3", "ext-json": "*", - "illuminate/database": "5.6.*|5.7.*|5.8.*", - "illuminate/support": "5.6.*|5.7.*|5.8.*", - "illuminate/queue": "5.6.*|5.7.*|5.8.*", + "illuminate/database": "5.7.*|5.8.*", + "illuminate/support": "5.7.*|5.8.*", + "illuminate/queue": "5.7.*|5.8.*", "enqueue/amqp-lib": "0.9.*", "queue-interop/amqp-interop": "0.8.*" }, "require-dev": { "phpunit/phpunit": "^7.0", - "illuminate/events": "5.6.*|5.7.*|5.8.*", + "illuminate/events": "5.7.*|5.8.*", "mockery/mockery": "^1.0", "laravel/horizon": "^3.0" }, @@ -35,7 +35,7 @@ }, "extra": { "branch-alias": { - "dev-master": "7.0-dev" + "dev-master": "8.0-dev" }, "laravel": { "providers": [ diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index 844aad16..bdbda96c 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -240,6 +240,13 @@ protected function getQueueName($queueName = null) return $queueName ?: $this->queueName; } + protected function createPayloadArray($job, $queue, $data = '') + { + return array_merge(parent::createPayloadArray($job, $queue, $data), [ + 'id' => $this->getRandomId(), + ]); + } + /** * Get a random ID string. * From 864b48cccd4fbd6a4e2f40b9296d29829a1eb44f Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Wed, 27 Mar 2019 23:16:44 +0300 Subject: [PATCH 060/375] update readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4b07bebe..7e740cb7 100644 --- a/README.md +++ b/README.md @@ -131,7 +131,7 @@ Once you completed the configuration you can use Laravel Queue API. If you used ## Laravel Horizon Usage -Starting with 7.4, this package supports [Laravel Horizon](http://horizon.laravel.com) out of the box. Firstly, install Horizon and then set `RABBITMQ_WORKER` to `horizon`. +Starting with 8.0, this package supports [Laravel Horizon](http://horizon.laravel.com) out of the box. Firstly, install Horizon and then set `RABBITMQ_WORKER` to `horizon`. ## Lumen Usage From d25817c4a2cf8af5667fd55cd1323d67da454ccc Mon Sep 17 00:00:00 2001 From: Francesco Carnielli Date: Fri, 10 May 2019 17:01:58 +0200 Subject: [PATCH 061/375] queue: add message expiration property --- src/Queue/RabbitMQQueue.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index bdbda96c..c99772f6 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -78,6 +78,10 @@ public function pushRaw($payload, $queueName = null, array $options = []) $message->setContentType('application/json'); $message->setDeliveryMode(AmqpMessage::DELIVERY_MODE_PERSISTENT); + if (isset($options['expiration'])) { + $message->setExpiration($options['expiration']); + } + if (isset($options['headers'])) { $message->setHeaders($options['headers']); } From ba427c094369cdfac5898801a8fee739d7708128 Mon Sep 17 00:00:00 2001 From: Francesco Carnielli Date: Mon, 13 May 2019 11:26:20 +0200 Subject: [PATCH 062/375] queue: add message routingKey property add missing properties --- src/Queue/RabbitMQQueue.php | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index c99772f6..c78dfe28 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -72,16 +72,35 @@ public function pushRaw($payload, $queueName = null, array $options = []) */ [$queue, $topic] = $this->declareEverything($queueName); + /** @var AmqpMessage $message */ $message = $this->context->createMessage($payload); - $message->setRoutingKey($queue->getQueueName()); + $message->setCorrelationId($this->getCorrelationId()); $message->setContentType('application/json'); $message->setDeliveryMode(AmqpMessage::DELIVERY_MODE_PERSISTENT); + if (isset($options['routing_key'])) { + $message->setRoutingKey($options['routing_key']); + } else { + $message->setRoutingKey($queue->getQueueName()); + } + + if (isset($options['priority'])) { + $message->setPriority($options['priority']); + } + if (isset($options['expiration'])) { $message->setExpiration($options['expiration']); } + if (isset($options['delivery_tag'])) { + $message->setDeliveryTag($options['delivery_tag']); + } + + if (isset($options['consumer_tag'])) { + $message->setConsumerTag($options['consumer_tag']); + } + if (isset($options['headers'])) { $message->setHeaders($options['headers']); } From 9e2d930876ff78437c5e9543d0d0d1b0d760d266 Mon Sep 17 00:00:00 2001 From: Francesco Carnielli Date: Wed, 15 May 2019 12:21:19 +0200 Subject: [PATCH 063/375] queue: remove delivery_tag and consumer_tag ping @makasim --- src/Queue/RabbitMQQueue.php | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index c78dfe28..d9b0f8a1 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -93,14 +93,6 @@ public function pushRaw($payload, $queueName = null, array $options = []) $message->setExpiration($options['expiration']); } - if (isset($options['delivery_tag'])) { - $message->setDeliveryTag($options['delivery_tag']); - } - - if (isset($options['consumer_tag'])) { - $message->setConsumerTag($options['consumer_tag']); - } - if (isset($options['headers'])) { $message->setHeaders($options['headers']); } From f6ff908f8f846bb0694fa1fc34d49d97628f0608 Mon Sep 17 00:00:00 2001 From: Stephan de Souza Date: Tue, 4 Jun 2019 13:48:49 -0300 Subject: [PATCH 064/375] Making RabbitMQQueue::declareEverything() public So we can use rabbit queue/topic instance outside the Queue. --- src/Queue/RabbitMQQueue.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index c78dfe28..1378a396 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -206,7 +206,7 @@ public function getContext(): AmqpContext * * @return array [Interop\Amqp\AmqpQueue, Interop\Amqp\AmqpTopic] */ - protected function declareEverything(string $queueName = null): array + public function declareEverything(string $queueName = null): array { $queueName = $this->getQueueName($queueName); $exchangeName = $this->exchangeOptions['name'] ?: $queueName; From 3f8969fdb0eb26cb557f7f0927df9b6d2a7b095a Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Sun, 28 Jul 2019 00:20:04 +0300 Subject: [PATCH 065/375] worker can be class that extends from RabbitMQQueue class --- src/Queue/Connectors/RabbitMQConnector.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Queue/Connectors/RabbitMQConnector.php b/src/Queue/Connectors/RabbitMQConnector.php index 59820fa7..f724191b 100644 --- a/src/Queue/Connectors/RabbitMQConnector.php +++ b/src/Queue/Connectors/RabbitMQConnector.php @@ -86,6 +86,10 @@ public function connect(array $config): Queue return new HorizonRabbitMQQueue($context, $config); } + if($worker instanceof RabbitMQQueue) { + return new $worker($context, $config); + } + throw new InvalidArgumentException('Invalid worker.'); } } From ca015171802426f09f153f3789e08219a6dba8c7 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Sun, 28 Jul 2019 00:20:54 +0300 Subject: [PATCH 066/375] worker can be class that extends from RabbitMQQueue class --- src/Queue/Connectors/RabbitMQConnector.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Queue/Connectors/RabbitMQConnector.php b/src/Queue/Connectors/RabbitMQConnector.php index f724191b..4685e9b6 100644 --- a/src/Queue/Connectors/RabbitMQConnector.php +++ b/src/Queue/Connectors/RabbitMQConnector.php @@ -86,7 +86,7 @@ public function connect(array $config): Queue return new HorizonRabbitMQQueue($context, $config); } - if($worker instanceof RabbitMQQueue) { + if ($worker instanceof RabbitMQQueue) { return new $worker($context, $config); } From da425b1ffe49a488f5d9fd2237bc555a8d9a6351 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Wed, 4 Sep 2019 13:11:47 +0300 Subject: [PATCH 067/375] support laravel 6 --- composer.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/composer.json b/composer.json index eec27d09..5774aade 100644 --- a/composer.json +++ b/composer.json @@ -11,15 +11,15 @@ "require": { "php": "^7.1.3", "ext-json": "*", - "illuminate/database": "5.7.*|5.8.*", - "illuminate/support": "5.7.*|5.8.*", - "illuminate/queue": "5.7.*|5.8.*", + "illuminate/database": "5.7.*|5.8.*|^6.0", + "illuminate/support": "5.7.*|5.8.*|^6.0", + "illuminate/queue": "5.7.*|5.8.*|^6.0", "enqueue/amqp-lib": "0.9.*", "queue-interop/amqp-interop": "0.8.*" }, "require-dev": { "phpunit/phpunit": "^7.0", - "illuminate/events": "5.7.*|5.8.*", + "illuminate/events": "5.7.*|5.8.*|^6.0", "mockery/mockery": "^1.0", "laravel/horizon": "^3.0" }, From 156fdcf4d35ab06585ac5c4a9244f3627d262a9a Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Wed, 4 Sep 2019 13:22:07 +0300 Subject: [PATCH 068/375] Revert "support laravel 6" This reverts commit da425b1f --- composer.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/composer.json b/composer.json index 5774aade..eec27d09 100644 --- a/composer.json +++ b/composer.json @@ -11,15 +11,15 @@ "require": { "php": "^7.1.3", "ext-json": "*", - "illuminate/database": "5.7.*|5.8.*|^6.0", - "illuminate/support": "5.7.*|5.8.*|^6.0", - "illuminate/queue": "5.7.*|5.8.*|^6.0", + "illuminate/database": "5.7.*|5.8.*", + "illuminate/support": "5.7.*|5.8.*", + "illuminate/queue": "5.7.*|5.8.*", "enqueue/amqp-lib": "0.9.*", "queue-interop/amqp-interop": "0.8.*" }, "require-dev": { "phpunit/phpunit": "^7.0", - "illuminate/events": "5.7.*|5.8.*|^6.0", + "illuminate/events": "5.7.*|5.8.*", "mockery/mockery": "^1.0", "laravel/horizon": "^3.0" }, From edd9cc43082d2846fe517db30aeb29a03b04b04e Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Wed, 4 Sep 2019 13:29:30 +0300 Subject: [PATCH 069/375] laravel 6 --- .travis.yml | 6 +----- composer.json | 12 ++++++------ src/Queue/Jobs/RabbitMQJob.php | 9 ++++----- 3 files changed, 11 insertions(+), 16 deletions(-) diff --git a/.travis.yml b/.travis.yml index 77adb2fd..0e18a000 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,13 +1,9 @@ language: php php: - - 7.1 - 7.2 - 7.3 -env: - - DOCKER_COMPOSE_VERSION=1.23.2 - services: - docker @@ -17,7 +13,7 @@ cache: before_install: - sudo rm /usr/local/bin/docker-compose - - curl -L https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_VERSION}/docker-compose-`uname -s`-`uname -m` > docker-compose + - curl -L https://github.com/docker/compose/releases/download/1.24.1/docker-compose-`uname -s`-`uname -m` > docker-compose - chmod +x docker-compose - sudo mv docker-compose /usr/local/bin diff --git a/composer.json b/composer.json index eec27d09..0a85b1ba 100644 --- a/composer.json +++ b/composer.json @@ -9,17 +9,17 @@ } ], "require": { - "php": "^7.1.3", + "php": "^7.2", "ext-json": "*", - "illuminate/database": "5.7.*|5.8.*", - "illuminate/support": "5.7.*|5.8.*", - "illuminate/queue": "5.7.*|5.8.*", + "illuminate/database": "^6.0", + "illuminate/support": "^6.0", + "illuminate/queue": "^6.0", "enqueue/amqp-lib": "0.9.*", "queue-interop/amqp-interop": "0.8.*" }, "require-dev": { "phpunit/phpunit": "^7.0", - "illuminate/events": "5.7.*|5.8.*", + "illuminate/events": "^6.0", "mockery/mockery": "^1.0", "laravel/horizon": "^3.0" }, @@ -35,7 +35,7 @@ }, "extra": { "branch-alias": { - "dev-master": "8.0-dev" + "dev-master": "9.0-dev" }, "laravel": { "providers": [ diff --git a/src/Queue/Jobs/RabbitMQJob.php b/src/Queue/Jobs/RabbitMQJob.php index dbcf7b14..b32cc527 100644 --- a/src/Queue/Jobs/RabbitMQJob.php +++ b/src/Queue/Jobs/RabbitMQJob.php @@ -9,14 +9,14 @@ use Interop\Amqp\AmqpConsumer; use Illuminate\Queue\Jobs\JobName; use Illuminate\Container\Container; -use Illuminate\Database\DetectsDeadlocks; +use Illuminate\Database\DetectsLostConnections; use Illuminate\Contracts\Queue\Job as JobContract; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue; use VladimirYuldashev\LaravelQueueRabbitMQ\Horizon\RabbitMQQueue as HorizonRabbitMQQueue; class RabbitMQJob extends Job implements JobContract { - use DetectsDeadlocks; + use DetectsLostConnections; /** * Same as RabbitMQQueue, used for attempt counts. @@ -58,7 +58,7 @@ public function fire(): void with($this->instance = $this->resolve($class))->{$method}($this, $payload['data']); } catch (Exception $exception) { if ( - $this->causedByDeadlock($exception) || + $this->causedByLostConnection($exception) || Str::contains($exception->getMessage(), ['detected deadlock']) ) { sleep(2); @@ -136,7 +136,6 @@ public function release($delay = 0): void * Get the job identifier. * * @return string - * @throws \Interop\Queue\Exception */ public function getJobId(): string { @@ -171,7 +170,7 @@ protected function unserialize(array $body) return unserialize($body['data']['command']); } catch (Exception $exception) { if ( - $this->causedByDeadlock($exception) || + $this->causedByLostConnection($exception) || Str::contains($exception->getMessage(), ['detected deadlock']) ) { sleep(2); From 3732e0d5caf18333eb420c4536c6edfe805fe786 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Wed, 4 Sep 2019 13:32:35 +0300 Subject: [PATCH 070/375] laravel 6 --- tests/Queue/Jobs/RabbitMQJobTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Queue/Jobs/RabbitMQJobTest.php b/tests/Queue/Jobs/RabbitMQJobTest.php index 6f4e67e7..025ddd6c 100644 --- a/tests/Queue/Jobs/RabbitMQJobTest.php +++ b/tests/Queue/Jobs/RabbitMQJobTest.php @@ -2,12 +2,12 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Queue\Jobs; +use Illuminate\Database\DetectsLostConnections; use Illuminate\Queue\Jobs\Job; use Interop\Amqp\AmqpConsumer; use PHPUnit\Framework\TestCase; use Interop\Amqp\Impl\AmqpMessage; use Illuminate\Container\Container; -use Illuminate\Database\DetectsDeadlocks; use Illuminate\Contracts\Queue\Job as JobContract; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Jobs\RabbitMQJob; @@ -32,7 +32,7 @@ public function testShouldUseDetectDeadlocksTrait() { $rc = new \ReflectionClass(RabbitMQJob::class); - $this->assertContains(DetectsDeadlocks::class, $rc->getTraitNames()); + $this->assertContains(DetectsLostConnections::class, $rc->getTraitNames()); } public function testCouldBeConstructedWithExpectedArguments() From 1f3be3206b13dee4177cdc254643b04503a26240 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Wed, 11 Sep 2019 15:16:18 +0300 Subject: [PATCH 071/375] wip github actions --- .github/workflows/main.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 .github/workflows/main.yml diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 00000000..33bcda7b --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,17 @@ +name: CI + +on: [push] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v1 + - name: Run a one-line script + run: echo Hello, world! + - name: Run a multi-line script + run: | + echo Add other actions to build, + echo test, and deploy your project. From c684edaf4817b1962e65ce5a7a65dff741462b28 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Wed, 11 Sep 2019 15:16:45 +0300 Subject: [PATCH 072/375] wip --- .github/workflows/main.yml | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 .github/workflows/main.yml diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml deleted file mode 100644 index 33bcda7b..00000000 --- a/.github/workflows/main.yml +++ /dev/null @@ -1,17 +0,0 @@ -name: CI - -on: [push] - -jobs: - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v1 - - name: Run a one-line script - run: echo Hello, world! - - name: Run a multi-line script - run: | - echo Add other actions to build, - echo test, and deploy your project. From 5a8da787422dd771f53df71ce01b8ea902a18317 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Wed, 11 Sep 2019 15:23:00 +0300 Subject: [PATCH 073/375] wip --- .github/workflows/main.yml | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 .github/workflows/main.yml diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml deleted file mode 100644 index 33bcda7b..00000000 --- a/.github/workflows/main.yml +++ /dev/null @@ -1,17 +0,0 @@ -name: CI - -on: [push] - -jobs: - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v1 - - name: Run a one-line script - run: echo Hello, world! - - name: Run a multi-line script - run: | - echo Add other actions to build, - echo test, and deploy your project. From 7358edd18b28091a50db4b60edddb60cd0aa5d6a Mon Sep 17 00:00:00 2001 From: Nenad Vasic Date: Fri, 4 Oct 2019 00:13:13 +0200 Subject: [PATCH 074/375] Fix return type of getJobId() method --- src/Queue/Jobs/RabbitMQJob.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Queue/Jobs/RabbitMQJob.php b/src/Queue/Jobs/RabbitMQJob.php index b32cc527..df484d07 100644 --- a/src/Queue/Jobs/RabbitMQJob.php +++ b/src/Queue/Jobs/RabbitMQJob.php @@ -135,9 +135,9 @@ public function release($delay = 0): void /** * Get the job identifier. * - * @return string + * @return string|null */ - public function getJobId(): string + public function getJobId(): ?string { return $this->message->getCorrelationId(); } From a7b3bb7649604c30029ff7a3ed7f5731116973c6 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Fri, 4 Oct 2019 11:01:58 +0000 Subject: [PATCH 075/375] Apply fixes from StyleCI --- tests/Queue/Jobs/RabbitMQJobTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Queue/Jobs/RabbitMQJobTest.php b/tests/Queue/Jobs/RabbitMQJobTest.php index 025ddd6c..b8ad9133 100644 --- a/tests/Queue/Jobs/RabbitMQJobTest.php +++ b/tests/Queue/Jobs/RabbitMQJobTest.php @@ -2,12 +2,12 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Queue\Jobs; -use Illuminate\Database\DetectsLostConnections; use Illuminate\Queue\Jobs\Job; use Interop\Amqp\AmqpConsumer; use PHPUnit\Framework\TestCase; use Interop\Amqp\Impl\AmqpMessage; use Illuminate\Container\Container; +use Illuminate\Database\DetectsLostConnections; use Illuminate\Contracts\Queue\Job as JobContract; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Jobs\RabbitMQJob; From 983fe145b3e5435ea6f000e13bdca8e3c04fcab8 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 7 Oct 2019 02:15:08 +0000 Subject: [PATCH 076/375] Update phpunit/phpunit requirement from ^7.0 to ^8.4 Updates the requirements on [phpunit/phpunit](https://github.com/sebastianbergmann/phpunit) to permit the latest version. - [Release notes](https://github.com/sebastianbergmann/phpunit/releases) - [Changelog](https://github.com/sebastianbergmann/phpunit/blob/master/ChangeLog-8.4.md) - [Commits](https://github.com/sebastianbergmann/phpunit/compare/7.0.0...8.4.0) Signed-off-by: dependabot-preview[bot] --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 0a85b1ba..37f72326 100644 --- a/composer.json +++ b/composer.json @@ -18,7 +18,7 @@ "queue-interop/amqp-interop": "0.8.*" }, "require-dev": { - "phpunit/phpunit": "^7.0", + "phpunit/phpunit": "^8.4", "illuminate/events": "^6.0", "mockery/mockery": "^1.0", "laravel/horizon": "^3.0" From d7dafddbbd303c75ac63e6b016cc0dd369f27f96 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Sun, 17 Nov 2019 15:45:14 +0300 Subject: [PATCH 077/375] JobAttemptsTest --- .gitignore | 3 +- composer.json | 3 +- tests/Functional/JobAttemptsTest.php | 107 +++++++++++++++++++++++++++ 3 files changed, 111 insertions(+), 2 deletions(-) create mode 100644 tests/Functional/JobAttemptsTest.php diff --git a/.gitignore b/.gitignore index e22b4623..eacfa385 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ /vendor composer.lock .phpstorm.meta.php -phpunit.xml \ No newline at end of file +phpunit.xml +.phpunit.result.cache diff --git a/composer.json b/composer.json index 37f72326..356dadda 100644 --- a/composer.json +++ b/composer.json @@ -21,7 +21,8 @@ "phpunit/phpunit": "^8.4", "illuminate/events": "^6.0", "mockery/mockery": "^1.0", - "laravel/horizon": "^3.0" + "laravel/horizon": "^3.0", + "larapack/dd": "^1.1" }, "autoload": { "psr-4": { diff --git a/tests/Functional/JobAttemptsTest.php b/tests/Functional/JobAttemptsTest.php new file mode 100644 index 00000000..b0d6fe29 --- /dev/null +++ b/tests/Functional/JobAttemptsTest.php @@ -0,0 +1,107 @@ + AmqpConnectionFactory::class, + 'dsn' => null, + 'host' => getenv('HOST'), + 'port' => getenv('PORT'), + 'login' => 'guest', + 'password' => 'guest', + 'vhost' => '/', + 'options' => [ + 'exchange' => [ + 'name' => null, + 'declare' => true, + 'type' => AmqpTopic::TYPE_DIRECT, + 'passive' => false, + 'durable' => true, + 'auto_delete' => false, + ], + + 'queue' => [ + 'name' => 'default', + 'declare' => true, + 'bind' => true, + 'passive' => false, + 'durable' => true, + 'exclusive' => false, + 'auto_delete' => false, + 'arguments' => '[]', + ], + ], + 'ssl_params' => [ + 'ssl_on' => false, + 'cafile' => null, + 'local_cert' => null, + 'local_key' => null, + 'verify_peer' => true, + 'passphrase' => null, + ], + ]; + + $connector = new RabbitMQConnector(new Dispatcher()); + /** @var RabbitMQQueue $queue */ + $queue = $connector->connect($config); + $queue->setContainer($this->createDummyContainer()); + + // we need it to declare exchange\queue on RabbitMQ side. + $queue->pushRaw('something'); + + $queue->getContext()->purgeQueue($queue->getContext()->createQueue('default')); + + $expectedPayload = __METHOD__.microtime(true); + + $queue->pushRaw($expectedPayload); + + sleep(1); + + $this->assertEquals(1, $queue->size()); + + $job = $queue->pop(); + + $this->assertInstanceOf(RabbitMQJob::class, $job); + $this->assertSame(1, $job->attempts()); + + $job->release(); + + $this->assertEquals(1, $queue->size()); + + $job = $queue->pop(); + + $this->assertInstanceOf(RabbitMQJob::class, $job); + $this->assertSame(2, $job->attempts()); + } + + private function createDummyContainer() + { + $container = new Container(); + $container['log'] = new NullLogger(); + + return $container; + } +} From 09b92cce2baf6a12e3f4ceb03839df1e4b2ff372 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Sun, 17 Nov 2019 12:46:26 +0000 Subject: [PATCH 078/375] Apply fixes from StyleCI --- src/Horizon/Listeners/RabbitMQFailedEvent.php | 2 +- src/Horizon/RabbitMQQueue.php | 8 ++++---- src/Queue/Connectors/RabbitMQConnector.php | 20 +++++++++---------- src/Queue/Jobs/RabbitMQJob.php | 14 ++++++------- src/Queue/RabbitMQQueue.php | 10 +++++----- .../SendAndReceiveDelayedMessageTest.php | 12 +++++------ .../Functional/SendAndReceiveMessageTest.php | 12 +++++------ tests/Functional/SslConnectionTest.php | 6 +++--- tests/Functional/StreamConnectionTest.php | 6 +++--- ...aravelQueueRabbitMQServiceProviderTest.php | 8 ++++---- tests/Mock/AmqpContextMock.php | 10 +++++----- ...yStrategyAwareAmqpConnectionFactorySpy.php | 2 +- .../Connectors/RabbitMQConnectorTest.php | 10 +++++----- tests/Queue/Jobs/RabbitMQJobTest.php | 10 +++++----- tests/Queue/RabbitMQQueueTest.php | 10 +++++----- 15 files changed, 70 insertions(+), 70 deletions(-) diff --git a/src/Horizon/Listeners/RabbitMQFailedEvent.php b/src/Horizon/Listeners/RabbitMQFailedEvent.php index e13445f4..9231ccab 100644 --- a/src/Horizon/Listeners/RabbitMQFailedEvent.php +++ b/src/Horizon/Listeners/RabbitMQFailedEvent.php @@ -3,8 +3,8 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Horizon\Listeners; use Illuminate\Contracts\Events\Dispatcher; -use Laravel\Horizon\Events\JobFailed as HorizonJobFailed; use Illuminate\Queue\Events\JobFailed as LaravelJobFailed; +use Laravel\Horizon\Events\JobFailed as HorizonJobFailed; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Jobs\RabbitMQJob; class RabbitMQFailedEvent diff --git a/src/Horizon/RabbitMQQueue.php b/src/Horizon/RabbitMQQueue.php index 69ef4060..5c5659cc 100644 --- a/src/Horizon/RabbitMQQueue.php +++ b/src/Horizon/RabbitMQQueue.php @@ -2,12 +2,12 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Horizon; -use Laravel\Horizon\JobId; -use Laravel\Horizon\JobPayload; -use Laravel\Horizon\Events\JobPushed; +use Illuminate\Contracts\Events\Dispatcher; use Laravel\Horizon\Events\JobDeleted; +use Laravel\Horizon\Events\JobPushed; use Laravel\Horizon\Events\JobReserved; -use Illuminate\Contracts\Events\Dispatcher; +use Laravel\Horizon\JobId; +use Laravel\Horizon\JobPayload; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Jobs\RabbitMQJob; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue as BaseRabbitMQQueue; diff --git a/src/Queue/Connectors/RabbitMQConnector.php b/src/Queue/Connectors/RabbitMQConnector.php index 4685e9b6..c6d0ea1d 100644 --- a/src/Queue/Connectors/RabbitMQConnector.php +++ b/src/Queue/Connectors/RabbitMQConnector.php @@ -2,22 +2,22 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Connectors; -use Illuminate\Support\Arr; -use Interop\Amqp\AmqpContext; -use InvalidArgumentException; -use Illuminate\Contracts\Queue\Queue; -use Illuminate\Queue\Events\JobFailed; -use Interop\Amqp\AmqpConnectionFactory; +use Enqueue\AmqpLib\AmqpConnectionFactory as EnqueueAmqpConnectionFactory; use Enqueue\AmqpTools\DelayStrategyAware; -use Illuminate\Contracts\Events\Dispatcher; -use Illuminate\Queue\Events\WorkerStopping; use Enqueue\AmqpTools\RabbitMqDlxDelayStrategy; +use Illuminate\Contracts\Events\Dispatcher; +use Illuminate\Contracts\Queue\Queue; use Illuminate\Queue\Connectors\ConnectorInterface; -use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue; +use Illuminate\Queue\Events\JobFailed; +use Illuminate\Queue\Events\WorkerStopping; +use Illuminate\Support\Arr; +use Interop\Amqp\AmqpConnectionFactory; use Interop\Amqp\AmqpConnectionFactory as InteropAmqpConnectionFactory; -use Enqueue\AmqpLib\AmqpConnectionFactory as EnqueueAmqpConnectionFactory; +use Interop\Amqp\AmqpContext; +use InvalidArgumentException; use VladimirYuldashev\LaravelQueueRabbitMQ\Horizon\Listeners\RabbitMQFailedEvent; use VladimirYuldashev\LaravelQueueRabbitMQ\Horizon\RabbitMQQueue as HorizonRabbitMQQueue; +use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue; class RabbitMQConnector implements ConnectorInterface { diff --git a/src/Queue/Jobs/RabbitMQJob.php b/src/Queue/Jobs/RabbitMQJob.php index df484d07..0312a921 100644 --- a/src/Queue/Jobs/RabbitMQJob.php +++ b/src/Queue/Jobs/RabbitMQJob.php @@ -3,16 +3,16 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Jobs; use Exception; -use Illuminate\Support\Str; -use Interop\Amqp\AmqpMessage; -use Illuminate\Queue\Jobs\Job; -use Interop\Amqp\AmqpConsumer; -use Illuminate\Queue\Jobs\JobName; use Illuminate\Container\Container; -use Illuminate\Database\DetectsLostConnections; use Illuminate\Contracts\Queue\Job as JobContract; -use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue; +use Illuminate\Database\DetectsLostConnections; +use Illuminate\Queue\Jobs\Job; +use Illuminate\Queue\Jobs\JobName; +use Illuminate\Support\Str; +use Interop\Amqp\AmqpConsumer; +use Interop\Amqp\AmqpMessage; use VladimirYuldashev\LaravelQueueRabbitMQ\Horizon\RabbitMQQueue as HorizonRabbitMQQueue; +use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue; class RabbitMQJob extends Job implements JobContract { diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index 02fc967e..c18d277e 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -2,16 +2,16 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Queue; -use RuntimeException; +use Illuminate\Contracts\Queue\Queue as QueueContract; use Illuminate\Queue\Queue; use Illuminate\Support\Str; -use Interop\Amqp\AmqpQueue; -use Interop\Amqp\AmqpTopic; -use Psr\Log\LoggerInterface; use Interop\Amqp\AmqpContext; use Interop\Amqp\AmqpMessage; +use Interop\Amqp\AmqpQueue; +use Interop\Amqp\AmqpTopic; use Interop\Amqp\Impl\AmqpBind; -use Illuminate\Contracts\Queue\Queue as QueueContract; +use Psr\Log\LoggerInterface; +use RuntimeException; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Jobs\RabbitMQJob; class RabbitMQQueue extends Queue implements QueueContract diff --git a/tests/Functional/SendAndReceiveDelayedMessageTest.php b/tests/Functional/SendAndReceiveDelayedMessageTest.php index 1a6ae946..e6213476 100644 --- a/tests/Functional/SendAndReceiveDelayedMessageTest.php +++ b/tests/Functional/SendAndReceiveDelayedMessageTest.php @@ -2,14 +2,14 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Functional; -use Psr\Log\NullLogger; -use PHPUnit\Framework\TestCase; -use Illuminate\Events\Dispatcher; -use Illuminate\Container\Container; use Enqueue\AmqpLib\AmqpConnectionFactory; -use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue; -use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Jobs\RabbitMQJob; +use Illuminate\Container\Container; +use Illuminate\Events\Dispatcher; +use PHPUnit\Framework\TestCase; +use Psr\Log\NullLogger; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Connectors\RabbitMQConnector; +use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Jobs\RabbitMQJob; +use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue; /** * @group functional diff --git a/tests/Functional/SendAndReceiveMessageTest.php b/tests/Functional/SendAndReceiveMessageTest.php index b5aa6a77..9a003932 100644 --- a/tests/Functional/SendAndReceiveMessageTest.php +++ b/tests/Functional/SendAndReceiveMessageTest.php @@ -2,14 +2,14 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Functional; -use Psr\Log\NullLogger; -use PHPUnit\Framework\TestCase; -use Illuminate\Events\Dispatcher; -use Illuminate\Container\Container; use Enqueue\AmqpLib\AmqpConnectionFactory; -use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue; -use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Jobs\RabbitMQJob; +use Illuminate\Container\Container; +use Illuminate\Events\Dispatcher; +use PHPUnit\Framework\TestCase; +use Psr\Log\NullLogger; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Connectors\RabbitMQConnector; +use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Jobs\RabbitMQJob; +use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue; /** * @group functional diff --git a/tests/Functional/SslConnectionTest.php b/tests/Functional/SslConnectionTest.php index c9a44068..be980f05 100644 --- a/tests/Functional/SslConnectionTest.php +++ b/tests/Functional/SslConnectionTest.php @@ -2,13 +2,13 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Functional; -use PHPUnit\Framework\TestCase; +use Enqueue\AmqpLib\AmqpConnectionFactory; use Enqueue\AmqpLib\AmqpContext; use Illuminate\Events\Dispatcher; -use Enqueue\AmqpLib\AmqpConnectionFactory; use PhpAmqpLib\Connection\AMQPSSLConnection; -use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue; +use PHPUnit\Framework\TestCase; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Connectors\RabbitMQConnector; +use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue; /** * @group functional diff --git a/tests/Functional/StreamConnectionTest.php b/tests/Functional/StreamConnectionTest.php index 74e55fb9..cf46d86a 100644 --- a/tests/Functional/StreamConnectionTest.php +++ b/tests/Functional/StreamConnectionTest.php @@ -2,13 +2,13 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Functional; -use PHPUnit\Framework\TestCase; +use Enqueue\AmqpLib\AmqpConnectionFactory; use Enqueue\AmqpLib\AmqpContext; use Illuminate\Events\Dispatcher; -use Enqueue\AmqpLib\AmqpConnectionFactory; use PhpAmqpLib\Connection\AMQPStreamConnection; -use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue; +use PHPUnit\Framework\TestCase; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Connectors\RabbitMQConnector; +use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue; /** * @group functional diff --git a/tests/LaravelQueueRabbitMQServiceProviderTest.php b/tests/LaravelQueueRabbitMQServiceProviderTest.php index de7eeba3..bf06f22d 100644 --- a/tests/LaravelQueueRabbitMQServiceProviderTest.php +++ b/tests/LaravelQueueRabbitMQServiceProviderTest.php @@ -2,13 +2,13 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Tests; -use PHPUnit\Framework\TestCase; -use Illuminate\Queue\QueueManager; use Illuminate\Container\Container; -use Illuminate\Support\ServiceProvider; use Illuminate\Contracts\Events\Dispatcher; -use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Connectors\RabbitMQConnector; +use Illuminate\Queue\QueueManager; +use Illuminate\Support\ServiceProvider; +use PHPUnit\Framework\TestCase; use VladimirYuldashev\LaravelQueueRabbitMQ\LaravelQueueRabbitMQServiceProvider; +use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Connectors\RabbitMQConnector; class LaravelQueueRabbitMQServiceProviderTest extends TestCase { diff --git a/tests/Mock/AmqpContextMock.php b/tests/Mock/AmqpContextMock.php index a1b392f9..5e80f20d 100644 --- a/tests/Mock/AmqpContextMock.php +++ b/tests/Mock/AmqpContextMock.php @@ -2,17 +2,17 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Mock; -use Interop\Queue\Queue; -use Interop\Queue\Topic; use Interop\Amqp\AmqpBind; -use Interop\Queue\Message; +use Interop\Amqp\AmqpContext; use Interop\Amqp\AmqpQueue; use Interop\Amqp\AmqpTopic; use Interop\Queue\Consumer; -use Interop\Queue\Producer; -use Interop\Amqp\AmqpContext; use Interop\Queue\Destination; +use Interop\Queue\Message; +use Interop\Queue\Producer; +use Interop\Queue\Queue; use Interop\Queue\SubscriptionConsumer; +use Interop\Queue\Topic; class AmqpContextMock implements AmqpContext { diff --git a/tests/Mock/DelayStrategyAwareAmqpConnectionFactorySpy.php b/tests/Mock/DelayStrategyAwareAmqpConnectionFactorySpy.php index 933952c7..b7f702dd 100644 --- a/tests/Mock/DelayStrategyAwareAmqpConnectionFactorySpy.php +++ b/tests/Mock/DelayStrategyAwareAmqpConnectionFactorySpy.php @@ -2,9 +2,9 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Mock; -use Interop\Queue\Context; use Enqueue\AmqpTools\DelayStrategy; use Enqueue\AmqpTools\DelayStrategyAware; +use Interop\Queue\Context; class DelayStrategyAwareAmqpConnectionFactorySpy implements \Interop\Amqp\AmqpConnectionFactory, DelayStrategyAware { diff --git a/tests/Queue/Connectors/RabbitMQConnectorTest.php b/tests/Queue/Connectors/RabbitMQConnectorTest.php index a2db6993..1aaf126e 100644 --- a/tests/Queue/Connectors/RabbitMQConnectorTest.php +++ b/tests/Queue/Connectors/RabbitMQConnectorTest.php @@ -2,14 +2,14 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Queue\Connectors; -use Interop\Amqp\AmqpContext; -use PHPUnit\Framework\TestCase; -use Illuminate\Contracts\Events\Dispatcher; -use Illuminate\Queue\Events\WorkerStopping; use Enqueue\AmqpTools\RabbitMqDlxDelayStrategy; +use Illuminate\Contracts\Events\Dispatcher; use Illuminate\Queue\Connectors\ConnectorInterface; -use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue; +use Illuminate\Queue\Events\WorkerStopping; +use Interop\Amqp\AmqpContext; +use PHPUnit\Framework\TestCase; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Connectors\RabbitMQConnector; +use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue; use VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Mock\AmqpConnectionFactorySpy; use VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Mock\CustomContextAmqpConnectionFactoryMock; use VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Mock\DelayStrategyAwareAmqpConnectionFactorySpy; diff --git a/tests/Queue/Jobs/RabbitMQJobTest.php b/tests/Queue/Jobs/RabbitMQJobTest.php index b8ad9133..eda07bcc 100644 --- a/tests/Queue/Jobs/RabbitMQJobTest.php +++ b/tests/Queue/Jobs/RabbitMQJobTest.php @@ -2,15 +2,15 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Queue\Jobs; +use Illuminate\Container\Container; +use Illuminate\Contracts\Queue\Job as JobContract; +use Illuminate\Database\DetectsLostConnections; use Illuminate\Queue\Jobs\Job; use Interop\Amqp\AmqpConsumer; -use PHPUnit\Framework\TestCase; use Interop\Amqp\Impl\AmqpMessage; -use Illuminate\Container\Container; -use Illuminate\Database\DetectsLostConnections; -use Illuminate\Contracts\Queue\Job as JobContract; -use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue; +use PHPUnit\Framework\TestCase; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Jobs\RabbitMQJob; +use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue; class RabbitMQJobTest extends TestCase { diff --git a/tests/Queue/RabbitMQQueueTest.php b/tests/Queue/RabbitMQQueueTest.php index ca9ebd8f..d1099472 100644 --- a/tests/Queue/RabbitMQQueueTest.php +++ b/tests/Queue/RabbitMQQueueTest.php @@ -2,15 +2,15 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Queue; -use Interop\Amqp\AmqpQueue; -use Interop\Amqp\AmqpTopic; -use Psr\Log\LoggerInterface; +use Illuminate\Container\Container; +use Interop\Amqp\AmqpConsumer; use Interop\Amqp\AmqpContext; use Interop\Amqp\AmqpMessage; -use Interop\Amqp\AmqpConsumer; use Interop\Amqp\AmqpProducer; +use Interop\Amqp\AmqpQueue; +use Interop\Amqp\AmqpTopic; use PHPUnit\Framework\TestCase; -use Illuminate\Container\Container; +use Psr\Log\LoggerInterface; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Jobs\RabbitMQJob; class RabbitMQQueueTest extends TestCase From 25f3d11d4a8d13ac88c0a2eb2012b108267354bf Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Tue, 19 Nov 2019 20:52:09 +0300 Subject: [PATCH 079/375] php-cs-fixer --- .gitignore | 1 + .php_cs.dist | 39 +++++++++++++++++++ composer.json | 3 +- src/Horizon/Listeners/RabbitMQFailedEvent.php | 4 +- src/LaravelQueueRabbitMQServiceProvider.php | 3 +- 5 files changed, 47 insertions(+), 3 deletions(-) create mode 100644 .php_cs.dist diff --git a/.gitignore b/.gitignore index eacfa385..00dea824 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ composer.lock .phpstorm.meta.php phpunit.xml .phpunit.result.cache +.php_cs.cache diff --git a/.php_cs.dist b/.php_cs.dist new file mode 100644 index 00000000..16d6e35a --- /dev/null +++ b/.php_cs.dist @@ -0,0 +1,39 @@ +in(['config', 'src', 'tests']); + +return PhpCsFixer\Config::create() + ->setFinder($finder) + ->setRules([ + 'psr0' => false, + '@PSR2' => true, + 'blank_line_after_namespace' => true, + 'braces' => true, + 'class_definition' => true, + 'elseif' => true, + 'function_declaration' => true, + 'indentation_type' => true, + 'line_ending' => true, + 'lowercase_constants' => true, + 'lowercase_keywords' => true, + 'method_argument_space' => [ + 'ensure_fully_multiline' => true,], + 'no_break_comment' => true, + 'no_closing_tag' => true, + 'no_spaces_after_function_name' => true, + 'no_spaces_inside_parenthesis' => true, + 'no_trailing_whitespace' => true, + 'no_trailing_whitespace_in_comment' => true, + 'single_blank_line_at_eof' => true, + 'single_class_element_per_statement' => [ + 'elements' => ['property'], + ], + 'single_import_per_statement' => true, + 'single_line_after_imports' => true, + 'switch_case_semicolon_to_colon' => true, + 'switch_case_space' => true, + 'visibility_required' => true, + 'encoding' => true, + 'full_opening_tag' => true, + ]); diff --git a/composer.json b/composer.json index 356dadda..1253847a 100644 --- a/composer.json +++ b/composer.json @@ -22,7 +22,8 @@ "illuminate/events": "^6.0", "mockery/mockery": "^1.0", "laravel/horizon": "^3.0", - "larapack/dd": "^1.1" + "larapack/dd": "^1.1", + "friendsofphp/php-cs-fixer": "^2.16" }, "autoload": { "psr-4": { diff --git a/src/Horizon/Listeners/RabbitMQFailedEvent.php b/src/Horizon/Listeners/RabbitMQFailedEvent.php index 9231ccab..3f30ca40 100644 --- a/src/Horizon/Listeners/RabbitMQFailedEvent.php +++ b/src/Horizon/Listeners/RabbitMQFailedEvent.php @@ -40,7 +40,9 @@ public function handle(LaravelJobFailed $event) } $this->events->dispatch((new HorizonJobFailed( - $event->exception, $event->job, $event->job->getRawBody() + $event->exception, + $event->job, + $event->job->getRawBody() ))->connection($event->connectionName)->queue($event->job->getQueue())); } } diff --git a/src/LaravelQueueRabbitMQServiceProvider.php b/src/LaravelQueueRabbitMQServiceProvider.php index 9d3d1184..ac210ee9 100644 --- a/src/LaravelQueueRabbitMQServiceProvider.php +++ b/src/LaravelQueueRabbitMQServiceProvider.php @@ -16,7 +16,8 @@ class LaravelQueueRabbitMQServiceProvider extends ServiceProvider public function register(): void { $this->mergeConfigFrom( - __DIR__.'/../config/rabbitmq.php', 'queue.connections.rabbitmq' + __DIR__.'/../config/rabbitmq.php', + 'queue.connections.rabbitmq' ); } From 6473970f1ceb6a29a9daf69950cdf206fc83824c Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Tue, 19 Nov 2019 20:54:35 +0300 Subject: [PATCH 080/375] add composer commands to fix code style --- README.md | 19 +++++++++++++++++-- composer.json | 8 +++++++- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 7e740cb7..cc980453 100644 --- a/README.md +++ b/README.md @@ -169,14 +169,29 @@ Change the factory class in `config/queue.php`: ## Testing Setup RabbitMQ using `docker-compose`: + ```bash docker-compose up -d ``` -Run tests: +To run the test suite you can use the following commands: -``` bash +```bash +# To run both style and unit tests. composer test + +# To run only style tests. +composer test:style + +# To run only unit tests. +composer test:unit +``` + +If you receive any errors from the style tests, you can automatically fix most, +if not all of the issues with the following command: + +```bash +composer fix:style ``` ## Contribution diff --git a/composer.json b/composer.json index 1253847a..ee04a10d 100644 --- a/composer.json +++ b/composer.json @@ -46,7 +46,13 @@ } }, "scripts": { - "test": "vendor/bin/phpunit" + "test": [ + "@test:style", + "@test:unit" + ], + "test:style": "@php vendor/bin/php-cs-fixer fix --config=.php_cs.dist --allow-risky=yes --dry-run --diff --verbose", + "test:unit": "@php vendor/bin/phpunit", + "fix:style": "@php vendor/bin/php-cs-fixer fix --config=.php_cs.dist --allow-risky=yes --diff --verbose" }, "minimum-stability": "dev", "prefer-stable": true From 0492e78f9df2084cd827355cd89912553e47199f Mon Sep 17 00:00:00 2001 From: "O. Lissenberg" Date: Wed, 20 Nov 2019 12:30:22 +0100 Subject: [PATCH 081/375] Add reconnect feature. Add styling rule. --- .php_cs.dist | 1 + src/Queue/Connectors/RabbitMQConnector.php | 67 +++++++++++++--------- src/Queue/RabbitMQQueue.php | 20 ++++++- 3 files changed, 60 insertions(+), 28 deletions(-) diff --git a/.php_cs.dist b/.php_cs.dist index 16d6e35a..12385eea 100644 --- a/.php_cs.dist +++ b/.php_cs.dist @@ -11,6 +11,7 @@ return PhpCsFixer\Config::create() 'blank_line_after_namespace' => true, 'braces' => true, 'class_definition' => true, + 'concat_space' => ['spacing' => 'none'], 'elseif' => true, 'function_declaration' => true, 'indentation_type' => true, diff --git a/src/Queue/Connectors/RabbitMQConnector.php b/src/Queue/Connectors/RabbitMQConnector.php index c6d0ea1d..7dcfdc0e 100644 --- a/src/Queue/Connectors/RabbitMQConnector.php +++ b/src/Queue/Connectors/RabbitMQConnector.php @@ -15,6 +15,8 @@ use Interop\Amqp\AmqpConnectionFactory as InteropAmqpConnectionFactory; use Interop\Amqp\AmqpContext; use InvalidArgumentException; +use LogicException; +use ReflectionClass; use VladimirYuldashev\LaravelQueueRabbitMQ\Horizon\Listeners\RabbitMQFailedEvent; use VladimirYuldashev\LaravelQueueRabbitMQ\Horizon\RabbitMQQueue as HorizonRabbitMQQueue; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue; @@ -41,34 +43,8 @@ public function __construct(Dispatcher $dispatcher) */ public function connect(array $config): Queue { - $factoryClass = Arr::get($config, 'factory_class', EnqueueAmqpConnectionFactory::class); - - if (! class_exists($factoryClass) || ! (new \ReflectionClass($factoryClass))->implementsInterface(InteropAmqpConnectionFactory::class)) { - throw new \LogicException(sprintf('The factory_class option has to be valid class that implements "%s"', InteropAmqpConnectionFactory::class)); - } - - /** @var AmqpConnectionFactory $factory */ - $factory = new $factoryClass([ - 'dsn' => Arr::get($config, 'dsn'), - 'host' => Arr::get($config, 'host', '127.0.0.1'), - 'port' => Arr::get($config, 'port', 5672), - 'user' => Arr::get($config, 'login', 'guest'), - 'pass' => Arr::get($config, 'password', 'guest'), - 'vhost' => Arr::get($config, 'vhost', '/'), - 'ssl_on' => Arr::get($config, 'ssl_params.ssl_on', false), - 'ssl_verify' => Arr::get($config, 'ssl_params.verify_peer', true), - 'ssl_cacert' => Arr::get($config, 'ssl_params.cafile'), - 'ssl_cert' => Arr::get($config, 'ssl_params.local_cert'), - 'ssl_key' => Arr::get($config, 'ssl_params.local_key'), - 'ssl_passphrase' => Arr::get($config, 'ssl_params.passphrase'), - ]); - - if ($factory instanceof DelayStrategyAware) { - $factory->setDelayStrategy(new RabbitMqDlxDelayStrategy()); - } - /** @var AmqpContext $context */ - $context = $factory->createContext(); + $context = self::createContext($config); $this->dispatcher->listen(WorkerStopping::class, function () use ($context) { $context->close(); @@ -92,4 +68,41 @@ public function connect(array $config): Queue throw new InvalidArgumentException('Invalid worker.'); } + + /** + * Create a context. + * + * @param array $config + * @return AmqpContext + */ + public static function createContext(array $config): AmqpContext + { + $factoryClass = Arr::get($config, 'factory_class', EnqueueAmqpConnectionFactory::class); + + if (! class_exists($factoryClass) || ! (new ReflectionClass($factoryClass))->implementsInterface(InteropAmqpConnectionFactory::class)) { + throw new LogicException(sprintf('The factory_class option has to be valid class that implements "%s"', InteropAmqpConnectionFactory::class)); + } + + /** @var AmqpConnectionFactory $factory */ + $factory = new $factoryClass([ + 'dsn' => Arr::get($config, 'dsn'), + 'host' => Arr::get($config, 'host', '127.0.0.1'), + 'port' => Arr::get($config, 'port', 5672), + 'user' => Arr::get($config, 'login', 'guest'), + 'pass' => Arr::get($config, 'password', 'guest'), + 'vhost' => Arr::get($config, 'vhost', '/'), + 'ssl_on' => Arr::get($config, 'ssl_params.ssl_on', false), + 'ssl_verify' => Arr::get($config, 'ssl_params.verify_peer', true), + 'ssl_cacert' => Arr::get($config, 'ssl_params.cafile'), + 'ssl_cert' => Arr::get($config, 'ssl_params.local_cert'), + 'ssl_key' => Arr::get($config, 'ssl_params.local_key'), + 'ssl_passphrase' => Arr::get($config, 'ssl_params.passphrase'), + ]); + + if ($factory instanceof DelayStrategyAware) { + $factory->setDelayStrategy(new RabbitMqDlxDelayStrategy()); + } + + return $factory->createContext(); + } } diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index c18d277e..137a73e2 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -10,8 +10,11 @@ use Interop\Amqp\AmqpQueue; use Interop\Amqp\AmqpTopic; use Interop\Amqp\Impl\AmqpBind; +use PhpAmqpLib\Exception\AMQPConnectionClosedException; use Psr\Log\LoggerInterface; use RuntimeException; +use Throwable; +use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Connectors\RabbitMQConnector; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Jobs\RabbitMQJob; class RabbitMQQueue extends Queue implements QueueContract @@ -29,10 +32,12 @@ class RabbitMQQueue extends Queue implements QueueContract * @var AmqpContext */ protected $context; + protected $config; protected $correlationId; public function __construct(AmqpContext $context, array $config) { + $this->config = $config; $this->context = $context; $this->queueName = $config['queue'] ?? $config['options']['queue']['name']; @@ -156,7 +161,20 @@ public function pop($queueName = null) if ($message = $consumer->receiveNoWait()) { return new RabbitMQJob($this->container, $this, $consumer, $message); } - } catch (\Throwable $exception) { + } catch (AMQPConnectionClosedException | AMQPChannelClosedException $connectionException) { + try { + // Connection/channel closed, attempt to reconnect. + $this->declaredExchanges = []; + $this->declaredQueues = []; + $this->context = RabbitMQConnector::createContext($this->config); + } catch (\Exception $e) { + // Silent reconnect attempt. + } + + $this->reportConnectionError('pop', $connectionException); + + return; + } catch (Throwable $exception) { $this->reportConnectionError('pop', $exception); return; From ef5a5489efbb1e8386a443a625e8f6dba0da0098 Mon Sep 17 00:00:00 2001 From: "O. Lissenberg" Date: Wed, 20 Nov 2019 15:08:24 +0100 Subject: [PATCH 082/375] Add missing AMQPChannelClosedException class use statement. --- src/Queue/RabbitMQQueue.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index 137a73e2..471bf43f 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -10,6 +10,7 @@ use Interop\Amqp\AmqpQueue; use Interop\Amqp\AmqpTopic; use Interop\Amqp\Impl\AmqpBind; +use PhpAmqpLib\Exception\AMQPChannelClosedException; use PhpAmqpLib\Exception\AMQPConnectionClosedException; use Psr\Log\LoggerInterface; use RuntimeException; @@ -167,7 +168,7 @@ public function pop($queueName = null) $this->declaredExchanges = []; $this->declaredQueues = []; $this->context = RabbitMQConnector::createContext($this->config); - } catch (\Exception $e) { + } catch (Throwable $e) { // Silent reconnect attempt. } From 9087e4645ff933d52bb507c0ae4b63bd8ccab026 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Wed, 20 Nov 2019 21:00:02 +0300 Subject: [PATCH 083/375] code style --- .php_cs.dist | 13 ++++++++++- src/Queue/Connectors/RabbitMQConnector.php | 22 +++++++++---------- .../SendAndReceiveDelayedMessageTest.php | 22 +++++++++---------- tests/Functional/SslConnectionTest.php | 22 +++++++++---------- tests/Functional/StreamConnectionTest.php | 22 +++++++++---------- .../Connectors/RabbitMQConnectorTest.php | 4 ++-- tests/Queue/RabbitMQQueueTest.php | 2 +- 7 files changed, 59 insertions(+), 48 deletions(-) diff --git a/.php_cs.dist b/.php_cs.dist index 12385eea..6ffd1f6b 100644 --- a/.php_cs.dist +++ b/.php_cs.dist @@ -4,6 +4,7 @@ $finder = PhpCsFixer\Finder::create() ->in(['config', 'src', 'tests']); return PhpCsFixer\Config::create() + ->setUsingCache(false) ->setFinder($finder) ->setRules([ 'psr0' => false, @@ -19,7 +20,8 @@ return PhpCsFixer\Config::create() 'lowercase_constants' => true, 'lowercase_keywords' => true, 'method_argument_space' => [ - 'ensure_fully_multiline' => true,], + 'ensure_fully_multiline' => true, + ], 'no_break_comment' => true, 'no_closing_tag' => true, 'no_spaces_after_function_name' => true, @@ -37,4 +39,13 @@ return PhpCsFixer\Config::create() 'visibility_required' => true, 'encoding' => true, 'full_opening_tag' => true, + 'blank_line_before_return' => true, + 'no_trailing_comma_in_singleline_array' => true, + 'trailing_comma_in_multiline_array' => true, + 'array_indentation' => true, + 'binary_operator_spaces' => [ + 'operators' => [ + '=' => 'single_space', + ], + ], ]); diff --git a/src/Queue/Connectors/RabbitMQConnector.php b/src/Queue/Connectors/RabbitMQConnector.php index 7dcfdc0e..1813cb13 100644 --- a/src/Queue/Connectors/RabbitMQConnector.php +++ b/src/Queue/Connectors/RabbitMQConnector.php @@ -85,17 +85,17 @@ public static function createContext(array $config): AmqpContext /** @var AmqpConnectionFactory $factory */ $factory = new $factoryClass([ - 'dsn' => Arr::get($config, 'dsn'), - 'host' => Arr::get($config, 'host', '127.0.0.1'), - 'port' => Arr::get($config, 'port', 5672), - 'user' => Arr::get($config, 'login', 'guest'), - 'pass' => Arr::get($config, 'password', 'guest'), - 'vhost' => Arr::get($config, 'vhost', '/'), - 'ssl_on' => Arr::get($config, 'ssl_params.ssl_on', false), - 'ssl_verify' => Arr::get($config, 'ssl_params.verify_peer', true), - 'ssl_cacert' => Arr::get($config, 'ssl_params.cafile'), - 'ssl_cert' => Arr::get($config, 'ssl_params.local_cert'), - 'ssl_key' => Arr::get($config, 'ssl_params.local_key'), + 'dsn' => Arr::get($config, 'dsn'), + 'host' => Arr::get($config, 'host', '127.0.0.1'), + 'port' => Arr::get($config, 'port', 5672), + 'user' => Arr::get($config, 'login', 'guest'), + 'pass' => Arr::get($config, 'password', 'guest'), + 'vhost' => Arr::get($config, 'vhost', '/'), + 'ssl_on' => Arr::get($config, 'ssl_params.ssl_on', false), + 'ssl_verify' => Arr::get($config, 'ssl_params.verify_peer', true), + 'ssl_cacert' => Arr::get($config, 'ssl_params.cafile'), + 'ssl_cert' => Arr::get($config, 'ssl_params.local_cert'), + 'ssl_key' => Arr::get($config, 'ssl_params.local_key'), 'ssl_passphrase' => Arr::get($config, 'ssl_params.passphrase'), ]); diff --git a/tests/Functional/SendAndReceiveDelayedMessageTest.php b/tests/Functional/SendAndReceiveDelayedMessageTest.php index e6213476..efd951b1 100644 --- a/tests/Functional/SendAndReceiveDelayedMessageTest.php +++ b/tests/Functional/SendAndReceiveDelayedMessageTest.php @@ -20,12 +20,12 @@ public function test() { $config = [ 'factory_class' => AmqpConnectionFactory::class, - 'dsn' => null, - 'host' => getenv('HOST'), - 'port' => getenv('PORT'), - 'login' => 'guest', + 'dsn' => null, + 'host' => getenv('HOST'), + 'port' => getenv('PORT'), + 'login' => 'guest', 'password' => 'guest', - 'vhost' => '/', + 'vhost' => '/', 'options' => [ 'exchange' => [ 'name' => null, @@ -48,12 +48,12 @@ public function test() ], ], 'ssl_params' => [ - 'ssl_on' => false, - 'cafile' => null, - 'local_cert' => null, - 'local_key' => null, - 'verify_peer' => true, - 'passphrase' => null, + 'ssl_on' => false, + 'cafile' => null, + 'local_cert' => null, + 'local_key' => null, + 'verify_peer' => true, + 'passphrase' => null, ], ]; diff --git a/tests/Functional/SslConnectionTest.php b/tests/Functional/SslConnectionTest.php index be980f05..4927d259 100644 --- a/tests/Functional/SslConnectionTest.php +++ b/tests/Functional/SslConnectionTest.php @@ -19,12 +19,12 @@ public function testConnectorEstablishSecureConnectionWithRabbitMQBroker() { $config = [ 'factory_class' => AmqpConnectionFactory::class, - 'dsn' => null, - 'host' => getenv('HOST'), - 'port' => getenv('PORT_SSL'), - 'login' => 'guest', + 'dsn' => null, + 'host' => getenv('HOST'), + 'port' => getenv('PORT_SSL'), + 'login' => 'guest', 'password' => 'guest', - 'vhost' => '/', + 'vhost' => '/', 'options' => [ 'exchange' => [ 'name' => null, @@ -47,12 +47,12 @@ public function testConnectorEstablishSecureConnectionWithRabbitMQBroker() ], ], 'ssl_params' => [ - 'ssl_on' => true, - 'cafile' => getenv('RABBITMQ_SSL_CAFILE'), - 'local_cert' => null, - 'local_key' => null, - 'verify_peer' => false, - 'passphrase' => null, + 'ssl_on' => true, + 'cafile' => getenv('RABBITMQ_SSL_CAFILE'), + 'local_cert' => null, + 'local_key' => null, + 'verify_peer' => false, + 'passphrase' => null, ], ]; diff --git a/tests/Functional/StreamConnectionTest.php b/tests/Functional/StreamConnectionTest.php index cf46d86a..7a878aa6 100644 --- a/tests/Functional/StreamConnectionTest.php +++ b/tests/Functional/StreamConnectionTest.php @@ -19,12 +19,12 @@ public function testConnectorEstablishSecureConnectionWithRabbitMQBroker() { $config = [ 'factory_class' => AmqpConnectionFactory::class, - 'dsn' => null, - 'host' => getenv('HOST'), - 'port' => getenv('PORT'), - 'login' => 'guest', + 'dsn' => null, + 'host' => getenv('HOST'), + 'port' => getenv('PORT'), + 'login' => 'guest', 'password' => 'guest', - 'vhost' => '/', + 'vhost' => '/', 'options' => [ 'exchange' => [ 'name' => null, @@ -47,12 +47,12 @@ public function testConnectorEstablishSecureConnectionWithRabbitMQBroker() ], ], 'ssl_params' => [ - 'ssl_on' => false, - 'cafile' => null, - 'local_cert' => null, - 'local_key' => null, - 'verify_peer' => true, - 'passphrase' => null, + 'ssl_on' => false, + 'cafile' => null, + 'local_cert' => null, + 'local_key' => null, + 'verify_peer' => true, + 'passphrase' => null, ], ]; diff --git a/tests/Queue/Connectors/RabbitMQConnectorTest.php b/tests/Queue/Connectors/RabbitMQConnectorTest.php index 1aaf126e..e52ae03f 100644 --- a/tests/Queue/Connectors/RabbitMQConnectorTest.php +++ b/tests/Queue/Connectors/RabbitMQConnectorTest.php @@ -152,8 +152,8 @@ private function createDummyConfig() 'verify_peer' => 'theVerifyPeer', 'cafile' => 'theCafile', 'local_cert' => 'theLocalCert', - 'local_key' => 'theLocalKey', - 'passphrase' => 'thePassPhrase', + 'local_key' => 'theLocalKey', + 'passphrase' => 'thePassPhrase', ], 'options' => [ 'exchange' => [ diff --git a/tests/Queue/RabbitMQQueueTest.php b/tests/Queue/RabbitMQQueueTest.php index d1099472..0d033586 100644 --- a/tests/Queue/RabbitMQQueueTest.php +++ b/tests/Queue/RabbitMQQueueTest.php @@ -427,7 +427,7 @@ private function createDummyConfig() 'verify_peer' => 'aVerifyPeer', 'cafile' => 'aCafile', 'local_cert' => 'aLocalCert', - 'local_key' => 'aLocalKey', + 'local_key' => 'aLocalKey', ], 'options' => [ 'exchange' => [ From 0a174288d7697d9e2ea2d82b70a66866787fc4e7 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Wed, 20 Nov 2019 21:00:13 +0300 Subject: [PATCH 084/375] 10.0-dev --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index ee04a10d..244377df 100644 --- a/composer.json +++ b/composer.json @@ -37,7 +37,7 @@ }, "extra": { "branch-alias": { - "dev-master": "9.0-dev" + "dev-master": "10.0-dev" }, "laravel": { "providers": [ From 204d254d4c345ee66db0a7ed7bf30145092cde4d Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Wed, 20 Nov 2019 22:22:34 +0300 Subject: [PATCH 085/375] refactor tests remove catch blocks from RabbitMQQueue class so exceptions will be thrown remove `reportConnectionError` method from RabbitMQQueue class --- .php_cs.dist | 2 + composer.json | 3 +- config/rabbitmq.php | 6 - src/Horizon/Listeners/RabbitMQFailedEvent.php | 8 +- src/Horizon/RabbitMQQueue.php | 11 +- src/Queue/Connectors/RabbitMQConnector.php | 8 +- src/Queue/RabbitMQQueue.php | 206 +++++++----------- tests/Feature/QueueTest.php | 19 ++ tests/Feature/SslQueueTest.php | 37 ++++ tests/Feature/TestCase.php | 101 +++++++++ tests/Functional/JobAttemptsTest.php | 107 --------- .../SendAndReceiveDelayedMessageTest.php | 95 -------- .../Functional/SendAndReceiveMessageTest.php | 95 -------- tests/Functional/SslConnectionTest.php | 72 ------ ...aravelQueueRabbitMQServiceProviderTest.php | 63 ------ tests/Mock/AmqpConnectionFactorySpy.php | 6 +- tests/Mock/AmqpContextMock.php | 33 +-- ...CustomContextAmqpConnectionFactoryMock.php | 3 +- ...yStrategyAwareAmqpConnectionFactorySpy.php | 6 +- tests/Mocks/TestJob.php | 12 + .../StreamConnectionTest.php => TestCase.php} | 59 ++--- .../Connectors/RabbitMQConnectorTest.php | 69 ++++-- .../{ => Unit}/Queue/Jobs/RabbitMQJobTest.php | 28 +-- tests/{ => Unit}/Queue/RabbitMQQueueTest.php | 159 ++++---------- tests/jobs/TestJob.php | 8 - 25 files changed, 430 insertions(+), 786 deletions(-) create mode 100644 tests/Feature/QueueTest.php create mode 100644 tests/Feature/SslQueueTest.php create mode 100644 tests/Feature/TestCase.php delete mode 100644 tests/Functional/JobAttemptsTest.php delete mode 100644 tests/Functional/SendAndReceiveDelayedMessageTest.php delete mode 100644 tests/Functional/SendAndReceiveMessageTest.php delete mode 100644 tests/Functional/SslConnectionTest.php delete mode 100644 tests/LaravelQueueRabbitMQServiceProviderTest.php create mode 100644 tests/Mocks/TestJob.php rename tests/{Functional/StreamConnectionTest.php => TestCase.php} (53%) rename tests/{ => Unit}/Queue/Connectors/RabbitMQConnectorTest.php (80%) rename tests/{ => Unit}/Queue/Jobs/RabbitMQJobTest.php (70%) rename tests/{ => Unit}/Queue/RabbitMQQueueTest.php (70%) delete mode 100644 tests/jobs/TestJob.php diff --git a/.php_cs.dist b/.php_cs.dist index 6ffd1f6b..e8f48f80 100644 --- a/.php_cs.dist +++ b/.php_cs.dist @@ -48,4 +48,6 @@ return PhpCsFixer\Config::create() '=' => 'single_space', ], ], + 'fully_qualified_strict_types' => true, + 'void_return' => true, ]); diff --git a/composer.json b/composer.json index 244377df..6094beb9 100644 --- a/composer.json +++ b/composer.json @@ -23,7 +23,8 @@ "mockery/mockery": "^1.0", "laravel/horizon": "^3.0", "larapack/dd": "^1.1", - "friendsofphp/php-cs-fixer": "^2.16" + "friendsofphp/php-cs-fixer": "^2.16", + "orchestra/testbench": "^4.3" }, "autoload": { "psr-4": { diff --git a/config/rabbitmq.php b/config/rabbitmq.php index 708d8bbc..02674904 100644 --- a/config/rabbitmq.php +++ b/config/rabbitmq.php @@ -77,12 +77,6 @@ ], ], - /* - * Determine the number of seconds to sleep if there's an error communicating with rabbitmq - * If set to false, it'll throw an exception rather than doing the sleep for X seconds. - */ - 'sleep_on_error' => env('RABBITMQ_ERROR_SLEEP', 5), - /* * Optional SSL params if an SSL connection is used */ diff --git a/src/Horizon/Listeners/RabbitMQFailedEvent.php b/src/Horizon/Listeners/RabbitMQFailedEvent.php index 3f30ca40..c034c341 100644 --- a/src/Horizon/Listeners/RabbitMQFailedEvent.php +++ b/src/Horizon/Listeners/RabbitMQFailedEvent.php @@ -12,14 +12,14 @@ class RabbitMQFailedEvent /** * The event dispatcher implementation. * - * @var \Illuminate\Contracts\Events\Dispatcher + * @var Dispatcher */ public $events; /** * Create a new listener instance. * - * @param \Illuminate\Contracts\Events\Dispatcher $events + * @param Dispatcher $events * @return void */ public function __construct(Dispatcher $events) @@ -30,10 +30,10 @@ public function __construct(Dispatcher $events) /** * Handle the event. * - * @param \Illuminate\Queue\Events\JobFailed $event + * @param LaravelJobFailed $event * @return void */ - public function handle(LaravelJobFailed $event) + public function handle(LaravelJobFailed $event): void { if (! $event->job instanceof RabbitMQJob) { return; diff --git a/src/Horizon/RabbitMQQueue.php b/src/Horizon/RabbitMQQueue.php index 5c5659cc..04f377e2 100644 --- a/src/Horizon/RabbitMQQueue.php +++ b/src/Horizon/RabbitMQQueue.php @@ -2,6 +2,7 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Horizon; +use Illuminate\Contracts\Container\BindingResolutionException; use Illuminate\Contracts\Events\Dispatcher; use Laravel\Horizon\Events\JobDeleted; use Laravel\Horizon\Events\JobPushed; @@ -44,7 +45,7 @@ public function pushRaw($payload, $queueName = null, array $options = []) { $payload = (new JobPayload($payload))->prepare($this->lastPushed)->value; - return tap(parent::pushRaw($payload, $queueName, $options), function () use ($queueName, $payload) { + return tap(parent::pushRaw($payload, $queueName, $options), function () use ($queueName, $payload): void { $this->event($this->getQueueName($queueName), new JobPushed($payload)); }); } @@ -54,7 +55,7 @@ public function later($delay, $job, $data = '', $queueName = null) { $payload = (new JobPayload($this->createPayload($job, $data)))->prepare($job)->value; - return tap(parent::pushRaw($payload, $queueName, ['delay' => $this->secondsUntil($delay)]), function () use ($payload, $queueName) { + return tap(parent::pushRaw($payload, $queueName, ['delay' => $this->secondsUntil($delay)]), function () use ($payload, $queueName): void { $this->event($this->getQueueName($queueName), new JobPushed($payload)); }); } @@ -62,7 +63,7 @@ public function later($delay, $job, $data = '', $queueName = null) /** {@inheritdoc} */ public function pop($queueName = null) { - return tap(parent::pop($queueName), function ($result) use ($queueName) { + return tap(parent::pop($queueName), function ($result) use ($queueName): void { if ($result instanceof RabbitMQJob) { $this->event($queueName ?: $this->queueName, new JobReserved($result->getRawBody())); } @@ -83,7 +84,7 @@ public function release($delay, $job, $data, $queue, $attempts = 0) * @param string $queueName * @param RabbitMQJob $job * @return void - * @throws \Illuminate\Contracts\Container\BindingResolutionException + * @throws BindingResolutionException */ public function deleteReserved($queueName, $job): void { @@ -96,7 +97,7 @@ public function deleteReserved($queueName, $job): void * @param string $queue * @param mixed $event * @return void - * @throws \Illuminate\Contracts\Container\BindingResolutionException + * @throws BindingResolutionException */ protected function event($queue, $event): void { diff --git a/src/Queue/Connectors/RabbitMQConnector.php b/src/Queue/Connectors/RabbitMQConnector.php index 1813cb13..1998b6f2 100644 --- a/src/Queue/Connectors/RabbitMQConnector.php +++ b/src/Queue/Connectors/RabbitMQConnector.php @@ -17,6 +17,7 @@ use InvalidArgumentException; use LogicException; use ReflectionClass; +use ReflectionException; use VladimirYuldashev\LaravelQueueRabbitMQ\Horizon\Listeners\RabbitMQFailedEvent; use VladimirYuldashev\LaravelQueueRabbitMQ\Horizon\RabbitMQQueue as HorizonRabbitMQQueue; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue; @@ -39,14 +40,14 @@ public function __construct(Dispatcher $dispatcher) * @param array $config * * @return Queue - * @throws \ReflectionException + * @throws ReflectionException */ public function connect(array $config): Queue { /** @var AmqpContext $context */ $context = self::createContext($config); - $this->dispatcher->listen(WorkerStopping::class, function () use ($context) { + $this->dispatcher->listen(WorkerStopping::class, function () use ($context): void { $context->close(); }); @@ -72,8 +73,9 @@ public function connect(array $config): Queue /** * Create a context. * - * @param array $config + * @param array $config * @return AmqpContext + * @throws ReflectionException */ public static function createContext(array $config): AmqpContext { diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index 471bf43f..5699d102 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -2,6 +2,8 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Queue; +use DateInterval; +use DateTimeInterface; use Illuminate\Contracts\Queue\Queue as QueueContract; use Illuminate\Queue\Queue; use Illuminate\Support\Str; @@ -10,18 +12,11 @@ use Interop\Amqp\AmqpQueue; use Interop\Amqp\AmqpTopic; use Interop\Amqp\Impl\AmqpBind; -use PhpAmqpLib\Exception\AMQPChannelClosedException; -use PhpAmqpLib\Exception\AMQPConnectionClosedException; -use Psr\Log\LoggerInterface; -use RuntimeException; -use Throwable; -use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Connectors\RabbitMQConnector; +use Interop\Queue\Exception; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Jobs\RabbitMQJob; class RabbitMQQueue extends Queue implements QueueContract { - protected $sleepOnError; - protected $queueName; protected $queueOptions; protected $exchangeOptions; @@ -49,11 +44,11 @@ public function __construct(AmqpContext $context, array $config) $this->exchangeOptions = $config['options']['exchange']; $this->exchangeOptions['arguments'] = isset($this->exchangeOptions['arguments']) ? json_decode($this->exchangeOptions['arguments'], true) : []; - - $this->sleepOnError = $config['sleep_on_error'] ?? 5; } - /** {@inheritdoc} */ + /** + * {@inheritdoc} + */ public function size($queueName = null): int { /** @var AmqpQueue $queue */ @@ -62,85 +57,84 @@ public function size($queueName = null): int return $this->context->declareQueue($queue); } - /** {@inheritdoc} */ + /** + * {@inheritdoc} + * + * @throws Exception + */ public function push($job, $data = '', $queue = null) { return $this->pushRaw($this->createPayload($job, $queue, $data), $queue, []); } - /** {@inheritdoc} */ + /** + * {@inheritdoc} + * + * @throws Exception + */ public function pushRaw($payload, $queueName = null, array $options = []) { - try { - /** - * @var AmqpTopic - * @var AmqpQueue $queue - */ - [$queue, $topic] = $this->declareEverything($queueName); - - /** @var AmqpMessage $message */ - $message = $this->context->createMessage($payload); - - $message->setCorrelationId($this->getCorrelationId()); - $message->setContentType('application/json'); - $message->setDeliveryMode(AmqpMessage::DELIVERY_MODE_PERSISTENT); - - if (isset($options['routing_key'])) { - $message->setRoutingKey($options['routing_key']); - } else { - $message->setRoutingKey($queue->getQueueName()); - } - - if (isset($options['priority'])) { - $message->setPriority($options['priority']); - } - - if (isset($options['expiration'])) { - $message->setExpiration($options['expiration']); - } - - if (isset($options['headers'])) { - $message->setHeaders($options['headers']); - } - - if (isset($options['properties'])) { - $message->setProperties($options['properties']); - } - - if (isset($options['attempts'])) { - $message->setProperty(RabbitMQJob::ATTEMPT_COUNT_HEADERS_KEY, $options['attempts']); - } - - $producer = $this->context->createProducer(); - if (isset($options['delay']) && $options['delay'] > 0) { - $producer->setDeliveryDelay($options['delay'] * 1000); - } - - $producer->send($topic, $message); - - return $message->getCorrelationId(); - } catch (\Exception $exception) { - $this->reportConnectionError('pushRaw', $exception); - - return; + /** + * @var AmqpTopic + * @var AmqpQueue $queue + */ + [$queue, $topic] = $this->declareEverything($queueName); + + /** @var AmqpMessage $message */ + $message = $this->context->createMessage($payload); + + $message->setCorrelationId($this->getCorrelationId()); + $message->setContentType('application/json'); + $message->setDeliveryMode(AmqpMessage::DELIVERY_MODE_PERSISTENT); + + if (isset($options['routing_key'])) { + $message->setRoutingKey($options['routing_key']); + } else { + $message->setRoutingKey($queue->getQueueName()); + } + + if (isset($options['priority'])) { + $message->setPriority($options['priority']); + } + + if (isset($options['expiration'])) { + $message->setExpiration($options['expiration']); + } + + if (isset($options['headers'])) { + $message->setHeaders($options['headers']); + } + + if (isset($options['properties'])) { + $message->setProperties($options['properties']); + } + + if (isset($options['attempts'])) { + $message->setProperty(RabbitMQJob::ATTEMPT_COUNT_HEADERS_KEY, $options['attempts']); + } + + $producer = $this->context->createProducer(); + if (isset($options['delay']) && $options['delay'] > 0) { + $producer->setDeliveryDelay($options['delay'] * 1000); } + + $producer->send($topic, $message); + + return $message->getCorrelationId(); } - /** {@inheritdoc} */ + /** + * {@inheritdoc} + * @throws Exception + */ public function later($delay, $job, $data = '', $queue = null) { return $this->pushRaw($this->createPayload($job, $queue, $data), $queue, ['delay' => $this->secondsUntil($delay)]); } /** - * Release a reserved job back onto the queue. - * - * @param \DateTimeInterface|\DateInterval|int $delay - * @param string|object $job - * @param mixed $data - * @param string $queue - * @param int $attempts - * @return mixed + * {@inheritDoc} + * @throws Exception */ public function release($delay, $job, $data, $queue, $attempts = 0) { @@ -150,36 +144,21 @@ public function release($delay, $job, $data, $queue, $attempts = 0) ]); } - /** {@inheritdoc} */ + /** + * {@inheritdoc} + */ public function pop($queueName = null) { - try { - /** @var AmqpQueue $queue */ - [$queue] = $this->declareEverything($queueName); - - $consumer = $this->context->createConsumer($queue); - - if ($message = $consumer->receiveNoWait()) { - return new RabbitMQJob($this->container, $this, $consumer, $message); - } - } catch (AMQPConnectionClosedException | AMQPChannelClosedException $connectionException) { - try { - // Connection/channel closed, attempt to reconnect. - $this->declaredExchanges = []; - $this->declaredQueues = []; - $this->context = RabbitMQConnector::createContext($this->config); - } catch (Throwable $e) { - // Silent reconnect attempt. - } - - $this->reportConnectionError('pop', $connectionException); - - return; - } catch (Throwable $exception) { - $this->reportConnectionError('pop', $exception); - - return; + /** @var AmqpQueue $queue */ + [$queue] = $this->declareEverything($queueName); + + $consumer = $this->context->createConsumer($queue); + + if ($message = $consumer->receiveNoWait()) { + return new RabbitMQJob($this->container, $this, $consumer, $message); } + + return null; } /** @@ -235,7 +214,7 @@ public function declareEverything(string $queueName = null): array $topic->addFlag(AmqpTopic::FLAG_AUTODELETE); } - if ($this->exchangeOptions['declare'] && ! in_array($exchangeName, $this->declaredExchanges, true)) { + if ($this->exchangeOptions['declare'] && !in_array($exchangeName, $this->declaredExchanges, true)) { $this->context->declareTopic($topic); $this->declaredExchanges[] = $exchangeName; @@ -256,7 +235,7 @@ public function declareEverything(string $queueName = null): array $queue->addFlag(AmqpQueue::FLAG_AUTODELETE); } - if ($this->queueOptions['declare'] && ! in_array($queueName, $this->declaredQueues, true)) { + if ($this->queueOptions['declare'] && !in_array($queueName, $this->declaredQueues, true)) { $this->context->declareQueue($queue); $this->declaredQueues[] = $queueName; @@ -290,25 +269,4 @@ protected function getRandomId(): string { return Str::random(32); } - - /** - * @param string $action - * @param \Throwable $e - * @throws \Exception - */ - protected function reportConnectionError($action, \Throwable $e) - { - /** @var LoggerInterface $logger */ - $logger = $this->container['log']; - - $logger->error('AMQP error while attempting '.$action.': '.$e->getMessage()); - - // If it's set to false, throw an error rather than waiting - if ($this->sleepOnError === false) { - throw new RuntimeException('Error writing data to the connection with RabbitMQ', null, $e); - } - - // Sleep so that we don't flood the log file - sleep($this->sleepOnError); - } } diff --git a/tests/Feature/QueueTest.php b/tests/Feature/QueueTest.php new file mode 100644 index 00000000..9ff21d0a --- /dev/null +++ b/tests/Feature/QueueTest.php @@ -0,0 +1,19 @@ +connection()->getContext(); + + $this->assertInstanceOf(AmqpContext::class, $context); + $this->assertInstanceOf(AMQPLazyConnection::class, $context->getLibChannel()->getConnection()); + $this->assertTrue($context->getLibChannel()->getConnection()->isConnected()); + } +} diff --git a/tests/Feature/SslQueueTest.php b/tests/Feature/SslQueueTest.php new file mode 100644 index 00000000..54e83c7f --- /dev/null +++ b/tests/Feature/SslQueueTest.php @@ -0,0 +1,37 @@ +set('queue.connections.rabbitmq.port', getenv('PORT_SSL')); + $app['config']->set('queue.connections.rabbitmq.ssl_params', [ + 'ssl_on' => true, + 'cafile' => getenv('RABBITMQ_SSL_CAFILE'), + 'local_cert' => null, + 'local_key' => null, + 'verify_peer' => false, + 'passphrase' => null, + ]); + } + + public function testConnection(): void + { + /** @var AmqpContext $context */ + $context = $this->connection()->getContext(); + + $this->assertInstanceOf(AmqpContext::class, $context); + $this->assertInstanceOf(AMQPSSLConnection::class, $context->getLibChannel()->getConnection()); + $this->assertTrue($context->getLibChannel()->getConnection()->isConnected()); + } +} diff --git a/tests/Feature/TestCase.php b/tests/Feature/TestCase.php new file mode 100644 index 00000000..626fc5b8 --- /dev/null +++ b/tests/Feature/TestCase.php @@ -0,0 +1,101 @@ +connection()->getContext()->purgeQueue( + $this->connection()->getContext()->createQueue('default') + ); + + $this->assertSame(0, Queue::size()); + + Queue::pushRaw($payload = Str::random()); + + $this->assertSame(1, Queue::size()); + + $this->assertNotNull($job = Queue::pop()); + + $this->assertInstanceOf(RabbitMQJob::class, $job); + $this->assertSame($payload, $job->getRawBody()); + + $job->delete(); + + $this->assertSame(0, Queue::size()); + } + + /** + * @throws PurgeQueueNotSupportedException + */ + public function testLater(): void + { + $this->connection()->getContext()->purgeQueue( + $this->connection()->getContext()->createQueue('default') + ); + + $this->assertSame(0, Queue::size()); + + $payload = Str::random(); + $data = [Str::random() => Str::random()]; + + Queue::later(3, $payload, $data); + + sleep(1); + + $this->assertSame(0, Queue::size()); + $this->assertNull(Queue::pop()); + + sleep(3); + + $this->assertSame(1, Queue::size()); + $this->assertNotNull($job = Queue::pop()); + + $this->assertInstanceOf(RabbitMQJob::class, $job); + $this->assertSame($payload, $job->getName()); + + $body = json_decode($job->getRawBody(), true); + + $this->assertSame($payload, $body['displayName']); + $this->assertSame($payload, $body['job']); + $this->assertSame($data, $body['data']); + + $job->delete(); + + $this->assertSame(0, Queue::size()); + } + + /** + * @throws PurgeQueueNotSupportedException + */ + public function testJobAttempts(): void + { + $this->connection()->getContext()->purgeQueue( + $this->connection()->getContext()->createQueue('default') + ); + + $expectedPayload = __METHOD__.microtime(true); + + Queue::pushRaw($expectedPayload); + + $job = Queue::pop(); + $this->assertSame(1, $job->attempts()); + + $job->release(); + + $job = Queue::pop(); + + $this->assertInstanceOf(RabbitMQJob::class, $job); + $this->assertSame(2, $job->attempts()); + } +} diff --git a/tests/Functional/JobAttemptsTest.php b/tests/Functional/JobAttemptsTest.php deleted file mode 100644 index b0d6fe29..00000000 --- a/tests/Functional/JobAttemptsTest.php +++ /dev/null @@ -1,107 +0,0 @@ - AmqpConnectionFactory::class, - 'dsn' => null, - 'host' => getenv('HOST'), - 'port' => getenv('PORT'), - 'login' => 'guest', - 'password' => 'guest', - 'vhost' => '/', - 'options' => [ - 'exchange' => [ - 'name' => null, - 'declare' => true, - 'type' => AmqpTopic::TYPE_DIRECT, - 'passive' => false, - 'durable' => true, - 'auto_delete' => false, - ], - - 'queue' => [ - 'name' => 'default', - 'declare' => true, - 'bind' => true, - 'passive' => false, - 'durable' => true, - 'exclusive' => false, - 'auto_delete' => false, - 'arguments' => '[]', - ], - ], - 'ssl_params' => [ - 'ssl_on' => false, - 'cafile' => null, - 'local_cert' => null, - 'local_key' => null, - 'verify_peer' => true, - 'passphrase' => null, - ], - ]; - - $connector = new RabbitMQConnector(new Dispatcher()); - /** @var RabbitMQQueue $queue */ - $queue = $connector->connect($config); - $queue->setContainer($this->createDummyContainer()); - - // we need it to declare exchange\queue on RabbitMQ side. - $queue->pushRaw('something'); - - $queue->getContext()->purgeQueue($queue->getContext()->createQueue('default')); - - $expectedPayload = __METHOD__.microtime(true); - - $queue->pushRaw($expectedPayload); - - sleep(1); - - $this->assertEquals(1, $queue->size()); - - $job = $queue->pop(); - - $this->assertInstanceOf(RabbitMQJob::class, $job); - $this->assertSame(1, $job->attempts()); - - $job->release(); - - $this->assertEquals(1, $queue->size()); - - $job = $queue->pop(); - - $this->assertInstanceOf(RabbitMQJob::class, $job); - $this->assertSame(2, $job->attempts()); - } - - private function createDummyContainer() - { - $container = new Container(); - $container['log'] = new NullLogger(); - - return $container; - } -} diff --git a/tests/Functional/SendAndReceiveDelayedMessageTest.php b/tests/Functional/SendAndReceiveDelayedMessageTest.php deleted file mode 100644 index efd951b1..00000000 --- a/tests/Functional/SendAndReceiveDelayedMessageTest.php +++ /dev/null @@ -1,95 +0,0 @@ - AmqpConnectionFactory::class, - 'dsn' => null, - 'host' => getenv('HOST'), - 'port' => getenv('PORT'), - 'login' => 'guest', - 'password' => 'guest', - 'vhost' => '/', - 'options' => [ - 'exchange' => [ - 'name' => null, - 'declare' => true, - 'type' => \Interop\Amqp\AmqpTopic::TYPE_DIRECT, - 'passive' => false, - 'durable' => true, - 'auto_delete' => false, - ], - - 'queue' => [ - 'name' => 'default', - 'declare' => true, - 'bind' => true, - 'passive' => false, - 'durable' => true, - 'exclusive' => false, - 'auto_delete' => false, - 'arguments' => '[]', - ], - ], - 'ssl_params' => [ - 'ssl_on' => false, - 'cafile' => null, - 'local_cert' => null, - 'local_key' => null, - 'verify_peer' => true, - 'passphrase' => null, - ], - ]; - - $connector = new RabbitMQConnector(new Dispatcher()); - /** @var RabbitMQQueue $queue */ - $queue = $connector->connect($config); - $queue->setContainer($this->createDummyContainer()); - - // we need it to declare exchange\queue on RabbitMQ side. - $queue->pushRaw('something'); - - $queue->getContext()->purgeQueue($queue->getContext()->createQueue('default')); - - $expectedPayload = __METHOD__.microtime(true); - - $queue->pushRaw($expectedPayload, null, ['delay' => 3]); - - sleep(1); - - $this->assertNull($queue->pop()); - - sleep(4); - - $job = $queue->pop(); - - $this->assertInstanceOf(RabbitMQJob::class, $job); - $this->assertSame($expectedPayload, $job->getRawBody()); - - $job->delete(); - } - - private function createDummyContainer() - { - $container = new Container(); - $container['log'] = new NullLogger(); - - return $container; - } -} diff --git a/tests/Functional/SendAndReceiveMessageTest.php b/tests/Functional/SendAndReceiveMessageTest.php deleted file mode 100644 index 9a003932..00000000 --- a/tests/Functional/SendAndReceiveMessageTest.php +++ /dev/null @@ -1,95 +0,0 @@ - AmqpConnectionFactory::class, - 'dsn' => null, - 'host' => getenv('HOST'), - 'port' => getenv('PORT'), - 'login' => 'guest', - 'password' => 'guest', - 'vhost' => '/', - 'options' => [ - 'exchange' => [ - 'name' => null, - 'declare' => true, - 'type' => \Interop\Amqp\AmqpTopic::TYPE_DIRECT, - 'passive' => false, - 'durable' => true, - 'auto_delete' => false, - ], - - 'queue' => [ - 'name' => 'default', - 'declare' => true, - 'bind' => true, - 'passive' => false, - 'durable' => true, - 'exclusive' => false, - 'auto_delete' => false, - 'arguments' => '[]', - ], - ], - 'ssl_params' => [ - 'ssl_on' => false, - 'cafile' => null, - 'local_cert' => null, - 'local_key' => null, - 'verify_peer' => true, - 'passphrase' => null, - ], - ]; - - $connector = new RabbitMQConnector(new Dispatcher()); - /** @var RabbitMQQueue $queue */ - $queue = $connector->connect($config); - $queue->setContainer($this->createDummyContainer()); - - // we need it to declare exchange\queue on RabbitMQ side. - $queue->pushRaw('something'); - - $queue->getContext()->purgeQueue($queue->getContext()->createQueue('default')); - - $expectedPayload = __METHOD__.microtime(true); - - $queue->pushRaw($expectedPayload); - - sleep(1); - - $this->assertEquals(1, $queue->size()); - - $job = $queue->pop(); - - $this->assertInstanceOf(RabbitMQJob::class, $job); - $this->assertSame($expectedPayload, $job->getRawBody()); - - $job->delete(); - - $this->assertEquals(0, $queue->size()); - } - - private function createDummyContainer() - { - $container = new Container(); - $container['log'] = new NullLogger(); - - return $container; - } -} diff --git a/tests/Functional/SslConnectionTest.php b/tests/Functional/SslConnectionTest.php deleted file mode 100644 index 4927d259..00000000 --- a/tests/Functional/SslConnectionTest.php +++ /dev/null @@ -1,72 +0,0 @@ - AmqpConnectionFactory::class, - 'dsn' => null, - 'host' => getenv('HOST'), - 'port' => getenv('PORT_SSL'), - 'login' => 'guest', - 'password' => 'guest', - 'vhost' => '/', - 'options' => [ - 'exchange' => [ - 'name' => null, - 'declare' => true, - 'type' => \Interop\Amqp\AmqpTopic::TYPE_DIRECT, - 'passive' => false, - 'durable' => true, - 'auto_delete' => false, - ], - - 'queue' => [ - 'name' => 'default', - 'declare' => true, - 'bind' => true, - 'passive' => false, - 'durable' => true, - 'exclusive' => false, - 'auto_delete' => false, - 'arguments' => '[]', - ], - ], - 'ssl_params' => [ - 'ssl_on' => true, - 'cafile' => getenv('RABBITMQ_SSL_CAFILE'), - 'local_cert' => null, - 'local_key' => null, - 'verify_peer' => false, - 'passphrase' => null, - ], - ]; - - $connector = new RabbitMQConnector(new Dispatcher()); - /** @var RabbitMQQueue $queue */ - $queue = $connector->connect($config); - - $this->assertInstanceOf(RabbitMQQueue::class, $queue); - - /** @var AmqpContext $context */ - $context = $queue->getContext(); - $this->assertInstanceOf(AmqpContext::class, $context); - - $this->assertInstanceOf(AMQPSSLConnection::class, $context->getLibChannel()->getConnection()); - $this->assertTrue($context->getLibChannel()->getConnection()->isConnected()); - } -} diff --git a/tests/LaravelQueueRabbitMQServiceProviderTest.php b/tests/LaravelQueueRabbitMQServiceProviderTest.php deleted file mode 100644 index bf06f22d..00000000 --- a/tests/LaravelQueueRabbitMQServiceProviderTest.php +++ /dev/null @@ -1,63 +0,0 @@ -assertTrue($rc->isSubclassOf(ServiceProvider::class)); - } - - public function testShouldMergeQueueConfigOnRegister() - { - $dir = realpath(__DIR__.'/../src'); - - //guard - $this->assertDirectoryExists($dir); - - $providerMock = $this->createPartialMock(LaravelQueueRabbitMQServiceProvider::class, ['mergeConfigFrom']); - - $providerMock - ->expects($this->once()) - ->method('mergeConfigFrom') - ->with($dir.'/../config/rabbitmq.php', 'queue.connections.rabbitmq'); - - $providerMock->register(); - } - - public function testShouldAddRabbitMQConnectorOnBoot() - { - $dispatcherMock = $this->createMock(Dispatcher::class); - - $queueMock = $this->createMock(QueueManager::class); - $queueMock - ->expects($this->once()) - ->method('addConnector') - ->with('rabbitmq', $this->isInstanceOf(\Closure::class)) - ->willReturnCallback(function ($driver, \Closure $resolver) use ($dispatcherMock) { - $connector = $resolver(); - - $this->assertInstanceOf(RabbitMQConnector::class, $connector); - $this->assertAttributeSame($dispatcherMock, 'dispatcher', $connector); - }); - - $app = Container::getInstance(); - $app['queue'] = $queueMock; - $app['events'] = $dispatcherMock; - - $providerMock = new LaravelQueueRabbitMQServiceProvider($app); - - $providerMock->boot(); - } -} diff --git a/tests/Mock/AmqpConnectionFactorySpy.php b/tests/Mock/AmqpConnectionFactorySpy.php index ced6b77b..b9d36583 100644 --- a/tests/Mock/AmqpConnectionFactorySpy.php +++ b/tests/Mock/AmqpConnectionFactorySpy.php @@ -2,11 +2,13 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Mock; +use Closure; +use Interop\Amqp\AmqpConnectionFactory; use Interop\Queue\Context; -class AmqpConnectionFactorySpy implements \Interop\Amqp\AmqpConnectionFactory +class AmqpConnectionFactorySpy implements AmqpConnectionFactory { - /** @var \Closure */ + /** @var Closure */ public static $spy; public function __construct($config) diff --git a/tests/Mock/AmqpContextMock.php b/tests/Mock/AmqpContextMock.php index 5e80f20d..62d05258 100644 --- a/tests/Mock/AmqpContextMock.php +++ b/tests/Mock/AmqpContextMock.php @@ -13,86 +13,87 @@ use Interop\Queue\Queue; use Interop\Queue\SubscriptionConsumer; use Interop\Queue\Topic; +use LogicException; class AmqpContextMock implements AmqpContext { public function createSubscriptionConsumer(): SubscriptionConsumer { - throw new \LogicException('It is not expected to be called'); + throw new LogicException('It is not expected to be called'); } public function declareTopic(AmqpTopic $topic): void { - throw new \LogicException('It is not expected to be called'); + throw new LogicException('It is not expected to be called'); } public function deleteTopic(AmqpTopic $topic): void { - throw new \LogicException('It is not expected to be called'); + throw new LogicException('It is not expected to be called'); } public function declareQueue(AmqpQueue $queue): int { - throw new \LogicException('It is not expected to be called'); + throw new LogicException('It is not expected to be called'); } public function deleteQueue(AmqpQueue $queue): void { - throw new \LogicException('It is not expected to be called'); + throw new LogicException('It is not expected to be called'); } public function purgeQueue(Queue $queue): void { - throw new \LogicException('It is not expected to be called'); + throw new LogicException('It is not expected to be called'); } public function bind(AmqpBind $bind): void { - throw new \LogicException('It is not expected to be called'); + throw new LogicException('It is not expected to be called'); } public function unbind(AmqpBind $bind): void { - throw new \LogicException('It is not expected to be called'); + throw new LogicException('It is not expected to be called'); } public function setQos(int $prefetchSize, int $prefetchCount, bool $global): void { - throw new \LogicException('It is not expected to be called'); + throw new LogicException('It is not expected to be called'); } public function close(): void { - throw new \LogicException('It is not expected to be called'); + throw new LogicException('It is not expected to be called'); } public function createQueue(string $queueName): Queue { - throw new \LogicException('It is not expected to be called'); + throw new LogicException('It is not expected to be called'); } public function createTemporaryQueue(): Queue { - throw new \LogicException('It is not expected to be called'); + throw new LogicException('It is not expected to be called'); } public function createProducer(): Producer { - throw new \LogicException('It is not expected to be called'); + throw new LogicException('It is not expected to be called'); } public function createConsumer(Destination $destination): Consumer { - throw new \LogicException('It is not expected to be called'); + throw new LogicException('It is not expected to be called'); } public function createTopic(string $amqpTopic): Topic { - throw new \LogicException('It is not expected to be called'); + throw new LogicException('It is not expected to be called'); } public function createMessage(string $body = '', array $properties = [], array $headers = []): Message { - throw new \LogicException('It is not expected to be called'); + throw new LogicException('It is not expected to be called'); } } diff --git a/tests/Mock/CustomContextAmqpConnectionFactoryMock.php b/tests/Mock/CustomContextAmqpConnectionFactoryMock.php index 11f28846..dc6e3879 100644 --- a/tests/Mock/CustomContextAmqpConnectionFactoryMock.php +++ b/tests/Mock/CustomContextAmqpConnectionFactoryMock.php @@ -2,9 +2,10 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Mock; +use Interop\Amqp\AmqpConnectionFactory; use Interop\Queue\Context; -class CustomContextAmqpConnectionFactoryMock implements \Interop\Amqp\AmqpConnectionFactory +class CustomContextAmqpConnectionFactoryMock implements AmqpConnectionFactory { public static $context; diff --git a/tests/Mock/DelayStrategyAwareAmqpConnectionFactorySpy.php b/tests/Mock/DelayStrategyAwareAmqpConnectionFactorySpy.php index b7f702dd..9ef5168e 100644 --- a/tests/Mock/DelayStrategyAwareAmqpConnectionFactorySpy.php +++ b/tests/Mock/DelayStrategyAwareAmqpConnectionFactorySpy.php @@ -2,13 +2,15 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Mock; +use Closure; use Enqueue\AmqpTools\DelayStrategy; use Enqueue\AmqpTools\DelayStrategyAware; +use Interop\Amqp\AmqpConnectionFactory; use Interop\Queue\Context; -class DelayStrategyAwareAmqpConnectionFactorySpy implements \Interop\Amqp\AmqpConnectionFactory, DelayStrategyAware +class DelayStrategyAwareAmqpConnectionFactorySpy implements AmqpConnectionFactory, DelayStrategyAware { - /** @var \Closure */ + /** @var Closure */ public static $spy; public function createContext(): Context diff --git a/tests/Mocks/TestJob.php b/tests/Mocks/TestJob.php new file mode 100644 index 00000000..bdd5f10a --- /dev/null +++ b/tests/Mocks/TestJob.php @@ -0,0 +1,12 @@ + AmqpConnectionFactory::class, + return [ + LaravelQueueRabbitMQServiceProvider::class, + ]; + } + + protected function getEnvironmentSetUp($app): void + { + $app['config']->set('queue.default', 'rabbitmq'); + $app['config']->set('queue.connections.rabbitmq', [ + 'driver' => 'rabbitmq', + 'worker' => 'default', 'dsn' => null, + 'factory_class' => AmqpConnectionFactory::class, 'host' => getenv('HOST'), 'port' => getenv('PORT'), + 'vhost' => '/', 'login' => 'guest', 'password' => 'guest', - 'vhost' => '/', + 'queue' => 'default', + 'options' => [ 'exchange' => [ 'name' => null, 'declare' => true, - 'type' => \Interop\Amqp\AmqpTopic::TYPE_DIRECT, + 'type' => AmqpTopic::TYPE_DIRECT, 'passive' => false, 'durable' => true, 'auto_delete' => false, + 'arguments' => '[]', ], 'queue' => [ - 'name' => 'default', 'declare' => true, 'bind' => true, 'passive' => false, @@ -46,6 +54,7 @@ public function testConnectorEstablishSecureConnectionWithRabbitMQBroker() 'arguments' => '[]', ], ], + 'ssl_params' => [ 'ssl_on' => false, 'cafile' => null, @@ -54,19 +63,11 @@ public function testConnectorEstablishSecureConnectionWithRabbitMQBroker() 'verify_peer' => true, 'passphrase' => null, ], - ]; - - $connector = new RabbitMQConnector(new Dispatcher()); - /** @var RabbitMQQueue $queue */ - $queue = $connector->connect($config); - - $this->assertInstanceOf(RabbitMQQueue::class, $queue); - - /** @var AmqpContext $context */ - $context = $queue->getContext(); - $this->assertInstanceOf(AmqpContext::class, $context); + ]); + } - $this->assertInstanceOf(AMQPStreamConnection::class, $context->getLibChannel()->getConnection()); - $this->assertTrue($context->getLibChannel()->getConnection()->isConnected()); + protected function connection(): RabbitMQQueue + { + return Queue::connection(); } } diff --git a/tests/Queue/Connectors/RabbitMQConnectorTest.php b/tests/Unit/Queue/Connectors/RabbitMQConnectorTest.php similarity index 80% rename from tests/Queue/Connectors/RabbitMQConnectorTest.php rename to tests/Unit/Queue/Connectors/RabbitMQConnectorTest.php index e52ae03f..b4a12a4a 100644 --- a/tests/Queue/Connectors/RabbitMQConnectorTest.php +++ b/tests/Unit/Queue/Connectors/RabbitMQConnectorTest.php @@ -1,13 +1,19 @@ assertTrue($rc->implementsInterface(ConnectorInterface::class)); } - public function testCouldBeConstructedWithDispatcherAsFirstArgument() + public function testCouldBeConstructedWithDispatcherAsFirstArgument(): void { new RabbitMQConnector($this->createMock(Dispatcher::class)); } - public function testThrowsIfFactoryClassIsNotValidClass() + /** + * @throws ReflectionException + */ + public function testThrowsIfFactoryClassIsNotValidClass(): void { + $this->expectException(LogicException::class); + $this->expectExceptionMessage('The factory_class option has to be valid class that implements "Interop\Amqp\AmqpConnectionFactory"'); + $connector = new RabbitMQConnector($this->createMock(Dispatcher::class)); - $this->expectException(\LogicException::class); - $this->expectExceptionMessage('The factory_class option has to be valid class that implements "Interop\Amqp\AmqpConnectionFactory"'); $connector->connect(['factory_class' => 'invalidClassName']); } - public function testThrowsIfFactoryClassDoesNotImplementConnectorFactoryInterface() + /** + * @throws ReflectionException + */ + public function testThrowsIfFactoryClassDoesNotImplementConnectorFactoryInterface(): void { + $this->expectException(LogicException::class); + $this->expectExceptionMessage('The factory_class option has to be valid class that implements "Interop\Amqp\AmqpConnectionFactory"'); + $connector = new RabbitMQConnector($this->createMock(Dispatcher::class)); - $this->expectException(\LogicException::class); - $this->expectExceptionMessage('The factory_class option has to be valid class that implements "Interop\Amqp\AmqpConnectionFactory"'); - $connector->connect(['factory_class' => \stdClass::class]); + $connector->connect(['factory_class' => stdClass::class]); } - public function testShouldPassExpectedConfigToConnectionFactory() + /** + * @throws ReflectionException + */ + public function testShouldPassExpectedConfigToConnectionFactory(): void { $called = false; - AmqpConnectionFactorySpy::$spy = function ($config) use (&$called) { + + AmqpConnectionFactorySpy::$spy = function ($config) use (&$called): void { $called = true; $this->assertEquals([ @@ -78,7 +96,10 @@ public function testShouldPassExpectedConfigToConnectionFactory() $this->assertTrue($called); } - public function testShouldReturnExpectedInstanceOfQueueOnConnect() + /** + * @throws ReflectionException + */ + public function testShouldReturnExpectedInstanceOfQueueOnConnect(): void { $connector = new RabbitMQConnector($this->createMock(Dispatcher::class)); @@ -90,12 +111,15 @@ public function testShouldReturnExpectedInstanceOfQueueOnConnect() $this->assertInstanceOf(RabbitMQQueue::class, $queue); } - public function testShouldSetRabbitMqDlxDelayStrategyIfConnectionFactoryImplementsDelayStrategyAwareInterface() + /** + * @throws ReflectionException + */ + public function testShouldSetRabbitMqDlxDelayStrategyIfConnectionFactoryImplementsDelayStrategyAwareInterface(): void { $connector = new RabbitMQConnector($this->createMock(Dispatcher::class)); $called = false; - DelayStrategyAwareAmqpConnectionFactorySpy::$spy = function ($actualStrategy) use (&$called) { + DelayStrategyAwareAmqpConnectionFactorySpy::$spy = function ($actualStrategy) use (&$called): void { $this->assertInstanceOf(RabbitMqDlxDelayStrategy::class, $actualStrategy); $called = true; @@ -109,7 +133,10 @@ public function testShouldSetRabbitMqDlxDelayStrategyIfConnectionFactoryImplemen $this->assertTrue($called); } - public function testShouldCallContextCloseMethodOnWorkerStoppingEvent() + /** + * @throws ReflectionException + */ + public function testShouldCallContextCloseMethodOnWorkerStoppingEvent(): void { $contextMock = $this->createMock(AmqpContext::class); $contextMock @@ -120,8 +147,8 @@ public function testShouldCallContextCloseMethodOnWorkerStoppingEvent() $dispatcherMock ->expects($this->once()) ->method('listen') - ->with(WorkerStopping::class, $this->isInstanceOf(\Closure::class)) - ->willReturnCallback(function ($eventName, \Closure $listener) { + ->with(WorkerStopping::class, $this->isInstanceOf(Closure::class)) + ->willReturnCallback(static function ($eventName, Closure $listener): void { $listener(); }); @@ -138,7 +165,7 @@ public function testShouldCallContextCloseMethodOnWorkerStoppingEvent() /** * @return array */ - private function createDummyConfig() + private function createDummyConfig(): array { return [ 'dsn' => 'theDsn', @@ -159,7 +186,7 @@ private function createDummyConfig() 'exchange' => [ 'name' => 'anExchangeName', 'declare' => false, - 'type' => \Interop\Amqp\AmqpTopic::TYPE_DIRECT, + 'type' => AmqpTopic::TYPE_DIRECT, 'passive' => false, 'durable' => true, 'auto_delete' => false, diff --git a/tests/Queue/Jobs/RabbitMQJobTest.php b/tests/Unit/Queue/Jobs/RabbitMQJobTest.php similarity index 70% rename from tests/Queue/Jobs/RabbitMQJobTest.php rename to tests/Unit/Queue/Jobs/RabbitMQJobTest.php index eda07bcc..f03a8645 100644 --- a/tests/Queue/Jobs/RabbitMQJobTest.php +++ b/tests/Unit/Queue/Jobs/RabbitMQJobTest.php @@ -1,43 +1,46 @@ assertTrue($rc->implementsInterface(JobContract::class)); } - public function testShouldBeSubClassOfQueue() + public function testShouldBeSubClassOfQueue(): void { - $rc = new \ReflectionClass(RabbitMQJob::class); + $rc = new ReflectionClass(RabbitMQJob::class); $this->assertTrue($rc->isSubclassOf(Job::class)); } - public function testShouldUseDetectDeadlocksTrait() + public function testShouldUseDetectDeadlocksTrait(): void { - $rc = new \ReflectionClass(RabbitMQJob::class); + $rc = new ReflectionClass(RabbitMQJob::class); $this->assertContains(DetectsLostConnections::class, $rc->getTraitNames()); } - public function testCouldBeConstructedWithExpectedArguments() + public function testCouldBeConstructedWithExpectedArguments(): void { - $queue = $this->createMock(\Interop\Amqp\AmqpQueue::class); + $queue = $this->createMock(AmqpQueue::class); $queue ->expects($this->once()) ->method('getQueueName') @@ -51,7 +54,6 @@ public function testCouldBeConstructedWithExpectedArguments() $connectionMock = $this->createRabbitMQQueueMock(); $connectionMock - ->expects($this->any()) ->method('getConnectionName') ->willReturn('theConnectionName'); @@ -62,12 +64,12 @@ public function testCouldBeConstructedWithExpectedArguments() new AmqpMessage() ); - $this->assertAttributeSame('theQueueName', 'queue', $job); + $this->assertSame('theQueueName', $job->getQueue()); $this->assertSame('theConnectionName', $job->getConnectionName()); } /** - * @return AmqpConsumer|\PHPUnit_Framework_MockObject_MockObject|AmqpConsumer + * @return AmqpConsumer|MockObject|AmqpConsumer */ private function createConsumerMock() { @@ -75,7 +77,7 @@ private function createConsumerMock() } /** - * @return \PHPUnit_Framework_MockObject_MockObject|RabbitMQQueue|RabbitMQQueue + * @return MockObject|RabbitMQQueue|RabbitMQQueue */ private function createRabbitMQQueueMock() { diff --git a/tests/Queue/RabbitMQQueueTest.php b/tests/Unit/Queue/RabbitMQQueueTest.php similarity index 70% rename from tests/Queue/RabbitMQQueueTest.php rename to tests/Unit/Queue/RabbitMQQueueTest.php index 0d033586..7128cce4 100644 --- a/tests/Queue/RabbitMQQueueTest.php +++ b/tests/Unit/Queue/RabbitMQQueueTest.php @@ -1,40 +1,46 @@ assertTrue($rc->implementsInterface(\Illuminate\Contracts\Queue\Queue::class)); + $this->assertTrue($rc->implementsInterface(QueueContract::class)); } - public function testShouldBeSubClassOfQueue() + public function testShouldBeSubClassOfQueue(): void { - $rc = new \ReflectionClass(RabbitMQQueue::class); + $rc = new ReflectionClass(RabbitMQQueue::class); - $this->assertTrue($rc->isSubclassOf(\Illuminate\Queue\Queue::class)); + $this->assertTrue($rc->isSubclassOf(Queue::class)); } - public function testCouldBeConstructedWithExpectedArguments() + public function testCouldBeConstructedWithExpectedArguments(): void { new RabbitMQQueue($this->createAmqpContext(), $this->createDummyConfig()); } - public function testShouldGenerateNewCorrelationIdIfNotSet() + public function testShouldGenerateNewCorrelationIdIfNotSet(): void { $queue = new RabbitMQQueue($this->createAmqpContext(), $this->createDummyConfig()); @@ -46,7 +52,7 @@ public function testShouldGenerateNewCorrelationIdIfNotSet() $this->assertNotSame($firstId, $secondId); } - public function testShouldReturnPreviouslySetCorrelationId() + public function testShouldReturnPreviouslySetCorrelationId(): void { $expectedId = 'theCorrelationId'; @@ -58,7 +64,7 @@ public function testShouldReturnPreviouslySetCorrelationId() $this->assertSame($expectedId, $queue->getCorrelationId()); } - public function testShouldAllowGetContextSetInConstructor() + public function testShouldAllowGetContextSetInConstructor(): void { $context = $this->createAmqpContext(); @@ -67,7 +73,7 @@ public function testShouldAllowGetContextSetInConstructor() $this->assertSame($context, $queue->getContext()); } - public function testShouldReturnExpectedNumberOfMessages() + public function testShouldReturnExpectedNumberOfMessages(): void { $expectedQueueName = 'theQueueName'; $queue = $this->createMock(AmqpQueue::class); @@ -95,21 +101,24 @@ public function testShouldReturnExpectedNumberOfMessages() $this->assertSame($expectedCount, $queue->size($expectedQueueName)); } - public function testShouldSendExpectedMessageOnPushRaw() + /** + * @throws Exception + */ + public function testShouldSendExpectedMessageOnPushRaw(): void { $expectedQueueName = 'theQueueName'; $expectedBody = 'thePayload'; $topic = $this->createMock(AmqpTopic::class); $queue = $this->createMock(AmqpQueue::class); - $queue->expects($this->any())->method('getQueueName')->willReturn('theQueueName'); + $queue->method('getQueueName')->willReturn('theQueueName'); $producer = $this->createMock(AmqpProducer::class); $producer ->expects($this->once()) ->method('send') ->with($this->identicalTo($topic), $this->isInstanceOf(AmqpMessage::class)) - ->willReturnCallback(function ($actualTopic, AmqpMessage $message) use ($expectedQueueName, $expectedBody, $topic) { + ->willReturnCallback(function ($actualTopic, AmqpMessage $message) use ($expectedQueueName, $expectedBody, $topic): void { $this->assertSame($topic, $actualTopic); $this->assertSame($expectedBody, $message->getBody()); $this->assertSame($expectedQueueName, $message->getRoutingKey()); @@ -149,7 +158,10 @@ public function testShouldSendExpectedMessageOnPushRaw() $queue->pushRaw('thePayload', $expectedQueueName); } - public function testShouldSetAttemptCountPropIfNotNull() + /** + * @throws Exception + */ + public function testShouldSetAttemptCountPropIfNotNull(): void { $expectedAttempts = 54321; @@ -160,7 +172,7 @@ public function testShouldSetAttemptCountPropIfNotNull() ->expects($this->once()) ->method('send') ->with($this->identicalTo($topic), $this->isInstanceOf(AmqpMessage::class)) - ->willReturnCallback(function ($actualTopic, AmqpMessage $message) use ($expectedAttempts) { + ->willReturnCallback(function ($actualTopic, AmqpMessage $message) use ($expectedAttempts): void { $this->assertSame($expectedAttempts, $message->getProperty(RabbitMQJob::ATTEMPT_COUNT_HEADERS_KEY)); }); $producer @@ -192,7 +204,10 @@ public function testShouldSetAttemptCountPropIfNotNull() $queue->pushRaw('thePayload', 'aQueue', ['attempts' => $expectedAttempts]); } - public function testShouldSetDeliveryDelayIfDelayOptionPresent() + /** + * @throws Exception + */ + public function testShouldSetDeliveryDelayIfDelayOptionPresent(): void { $expectedDelay = 56; $expectedDeliveryDelay = 56000; @@ -233,52 +248,7 @@ public function testShouldSetDeliveryDelayIfDelayOptionPresent() $queue->pushRaw('thePayload', 'aQueue', ['delay' => $expectedDelay]); } - public function testShouldLogExceptionOnPushRaw() - { - $producer = $this->createMock(AmqpProducer::class); - $producer - ->expects($this->once()) - ->method('send') - ->willReturnCallback(function () { - throw new \LogicException('Something went wrong while sending a message'); - }); - - $context = $this->createAmqpContext(); - $context - ->expects($this->once()) - ->method('createTopic') - ->willReturn($this->createMock(AmqpTopic::class)); - $context - ->expects($this->once()) - ->method('createMessage') - ->willReturn($this->createMock(AmqpMessage::class)); - $context - ->expects($this->once()) - ->method('createQueue') - ->willReturn($this->createMock(AmqpQueue::class)); - $context - ->expects($this->once()) - ->method('createProducer') - ->willReturn($producer); - - $logger = $this->createMock(LoggerInterface::class); - $logger - ->expects($this->once()) - ->method('error') - ->with('AMQP error while attempting pushRaw: Something went wrong while sending a message'); - - $container = new Container(); - $container['log'] = $logger; - - $queue = new RabbitMQQueue($context, $this->createDummyConfig()); - $queue->setContainer($container); - - $this->expectException(\RuntimeException::class); - $this->expectExceptionMessage('Error writing data to the connection with RabbitMQ'); - $queue->pushRaw('thePayload', 'aQueue'); - } - - public function testShouldReturnNullIfNoMessagesOnQueue() + public function testShouldReturnNullIfNoMessagesOnQueue(): void { $queue = $this->createMock(AmqpQueue::class); @@ -309,7 +279,7 @@ public function testShouldReturnNullIfNoMessagesOnQueue() $this->assertNull($queue->pop('aQueue')); } - public function testShouldReturnRabbitMQJobIfMessageReceivedFromQueue() + public function testShouldReturnRabbitMQJobIfMessageReceivedFromQueue(): void { $queue = $this->createMock(AmqpQueue::class); @@ -348,72 +318,23 @@ public function testShouldReturnRabbitMQJobIfMessageReceivedFromQueue() $this->assertInstanceOf(RabbitMQJob::class, $job); } - public function testShouldLogExceptionOnPop() - { - $queue = $this->createMock(AmqpQueue::class); - - $consumer = $this->createMock(AmqpConsumer::class); - $consumer - ->expects($this->once()) - ->method('receiveNoWait') - ->willReturnCallback(function () { - throw new \LogicException('Something went wrong while receiving a message'); - }); - - $context = $this->createAmqpContext(); - $context - ->expects($this->once()) - ->method('createTopic') - ->willReturn($this->createMock(AmqpTopic::class)); - $context - ->expects($this->once()) - ->method('createQueue') - ->willReturn($queue); - $context - ->expects($this->once()) - ->method('createConsumer') - ->with($this->identicalTo($queue)) - ->willReturn($consumer); - - $logger = $this->createMock(LoggerInterface::class); - $logger - ->expects($this->once()) - ->method('error') - ->with('AMQP error while attempting pop: Something went wrong while receiving a message'); - - $container = new Container(); - $container['log'] = $logger; - - $queue = new RabbitMQQueue($context, $this->createDummyConfig()); - $queue->setContainer($container); - - $this->expectException(\RuntimeException::class); - $this->expectExceptionMessage('Error writing data to the connection with RabbitMQ'); - $queue->pop('aQueue'); - } - /** - * @return AmqpContext|\PHPUnit_Framework_MockObject_MockObject|AmqpContext + * @return AmqpContext|MockObject|AmqpContext */ private function createAmqpContext() { return $this->createMock(AmqpContext::class); } - private function createDummyContainer() + private function createDummyContainer(): Container { - $logger = $this->createMock(LoggerInterface::class); - - $container = new Container(); - $container['log'] = $logger; - - return $container; + return new Container(); } /** * @return array */ - private function createDummyConfig() + private function createDummyConfig(): array { return [ 'dsn' => 'aDsn', @@ -433,7 +354,7 @@ private function createDummyConfig() 'exchange' => [ 'name' => 'anExchangeName', 'declare' => false, - 'type' => \Interop\Amqp\AmqpTopic::TYPE_DIRECT, + 'type' => AmqpTopic::TYPE_DIRECT, 'passive' => false, 'durable' => true, 'auto_delete' => false, diff --git a/tests/jobs/TestJob.php b/tests/jobs/TestJob.php deleted file mode 100644 index a0439470..00000000 --- a/tests/jobs/TestJob.php +++ /dev/null @@ -1,8 +0,0 @@ - Date: Wed, 20 Nov 2019 22:37:40 +0300 Subject: [PATCH 086/375] fix tests --- tests/Feature/TestCase.php | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/tests/Feature/TestCase.php b/tests/Feature/TestCase.php index 626fc5b8..2a01e6b5 100644 --- a/tests/Feature/TestCase.php +++ b/tests/Feature/TestCase.php @@ -13,12 +13,19 @@ abstract class TestCase extends BaseTestCase /** * @throws PurgeQueueNotSupportedException */ - public function testPush(): void + protected function setUp(): void { + parent::setUp(); + + $this->connection()->declareEverything('default'); + $this->connection()->getContext()->purgeQueue( $this->connection()->getContext()->createQueue('default') ); + } + public function testPush(): void + { $this->assertSame(0, Queue::size()); Queue::pushRaw($payload = Str::random()); @@ -35,15 +42,8 @@ public function testPush(): void $this->assertSame(0, Queue::size()); } - /** - * @throws PurgeQueueNotSupportedException - */ public function testLater(): void { - $this->connection()->getContext()->purgeQueue( - $this->connection()->getContext()->createQueue('default') - ); - $this->assertSame(0, Queue::size()); $payload = Str::random(); @@ -75,15 +75,8 @@ public function testLater(): void $this->assertSame(0, Queue::size()); } - /** - * @throws PurgeQueueNotSupportedException - */ public function testJobAttempts(): void { - $this->connection()->getContext()->purgeQueue( - $this->connection()->getContext()->createQueue('default') - ); - $expectedPayload = __METHOD__.microtime(true); Queue::pushRaw($expectedPayload); From e510964381b6045600721144bd9d504f263312fe Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Wed, 20 Nov 2019 22:39:04 +0300 Subject: [PATCH 087/375] add php 7.4snapshot to travis --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 0e18a000..d64690fa 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,7 @@ language: php php: - 7.2 - 7.3 + - 7.4snapshot services: - docker From c8158f01305ffc353a038814e58c0d96b8ca7002 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Wed, 20 Nov 2019 22:40:25 +0300 Subject: [PATCH 088/375] small cleanup --- tests/{Mock => Mocks}/AmqpConnectionFactorySpy.php | 2 +- tests/{Mock => Mocks}/AmqpContextMock.php | 2 +- .../CustomContextAmqpConnectionFactoryMock.php | 2 +- .../DelayStrategyAwareAmqpConnectionFactorySpy.php | 2 +- tests/Unit/Queue/Connectors/RabbitMQConnectorTest.php | 6 +++--- 5 files changed, 7 insertions(+), 7 deletions(-) rename tests/{Mock => Mocks}/AmqpConnectionFactorySpy.php (86%) rename tests/{Mock => Mocks}/AmqpContextMock.php (97%) rename tests/{Mock => Mocks}/CustomContextAmqpConnectionFactoryMock.php (81%) rename tests/{Mock => Mocks}/DelayStrategyAwareAmqpConnectionFactorySpy.php (91%) diff --git a/tests/Mock/AmqpConnectionFactorySpy.php b/tests/Mocks/AmqpConnectionFactorySpy.php similarity index 86% rename from tests/Mock/AmqpConnectionFactorySpy.php rename to tests/Mocks/AmqpConnectionFactorySpy.php index b9d36583..dbb16447 100644 --- a/tests/Mock/AmqpConnectionFactorySpy.php +++ b/tests/Mocks/AmqpConnectionFactorySpy.php @@ -1,6 +1,6 @@ Date: Mon, 25 Nov 2019 16:30:48 +0300 Subject: [PATCH 089/375] wip --- .gitattributes | 15 + README.md | 34 +- composer.json | 6 +- config/rabbitmq.php | 91 +--- docker-compose.yml | 14 +- phpunit.xml.dist | 2 +- src/Console/ConsumeCommand.php | 56 +++ src/Console/ExchangeMakeCommand.php | 62 +++ src/Console/QueueBindCommand.php | 36 ++ src/Console/QueueMakeCommand.php | 61 +++ src/Console/QueuePurgeCommand.php | 11 + src/Horizon/RabbitMQQueue.php | 40 +- src/LaravelQueueRabbitMQServiceProvider.php | 11 + src/Queue/Connectors/RabbitMQConnector.php | 71 +--- src/Queue/Jobs/RabbitMQJob.php | 187 ++++----- src/Queue/RabbitMQQueue.php | 396 ++++++++++-------- tests/Feature/QueueTest.php | 8 +- tests/Feature/SslQueueTest.php | 47 ++- tests/Feature/TestCase.php | 220 +++++++++- tests/Mocks/AmqpConnectionFactorySpy.php | 25 -- tests/Mocks/AmqpContextMock.php | 99 ----- ...CustomContextAmqpConnectionFactoryMock.php | 16 - ...yStrategyAwareAmqpConnectionFactorySpy.php | 34 -- tests/Mocks/TestJob.php | 3 +- tests/TestCase.php | 56 +-- .../Connectors/RabbitMQConnectorTest.php | 209 --------- tests/Unit/Queue/Jobs/RabbitMQJobTest.php | 86 ---- tests/Unit/Queue/RabbitMQQueueTest.php | 377 ----------------- 28 files changed, 890 insertions(+), 1383 deletions(-) create mode 100644 .gitattributes create mode 100644 src/Console/ConsumeCommand.php create mode 100644 src/Console/ExchangeMakeCommand.php create mode 100644 src/Console/QueueBindCommand.php create mode 100644 src/Console/QueueMakeCommand.php create mode 100644 src/Console/QueuePurgeCommand.php delete mode 100644 tests/Mocks/AmqpConnectionFactorySpy.php delete mode 100644 tests/Mocks/AmqpContextMock.php delete mode 100644 tests/Mocks/CustomContextAmqpConnectionFactoryMock.php delete mode 100644 tests/Mocks/DelayStrategyAwareAmqpConnectionFactorySpy.php delete mode 100644 tests/Unit/Queue/Connectors/RabbitMQConnectorTest.php delete mode 100644 tests/Unit/Queue/Jobs/RabbitMQJobTest.php delete mode 100644 tests/Unit/Queue/RabbitMQQueueTest.php diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..9e5277b7 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,15 @@ +* text=auto + +/.github export-ignore +/tests export-ignore +.editorconfig export-ignore +.gitattributes export-ignore +.gitignore export-ignore +.styleci.yml export-ignore +.travis.yml export-ignore +.php_cs.dist export-ignore +CHANGELOG-* export-ignore +CODE_OF_CONDUCT.md export-ignore +CONTRIBUTING.md export-ignore +phpunit.xml.dist export-ignore +docker-compose.yml export-ignore diff --git a/README.md b/README.md index cc980453..d9b8925f 100644 --- a/README.md +++ b/README.md @@ -141,30 +141,16 @@ For Lumen usage the service provider should be registered manually as follow in $app->register(VladimirYuldashev\LaravelQueueRabbitMQ\LaravelQueueRabbitMQServiceProvider::class); ``` - -## Using other AMQP transports - -The package uses [enqueue/amqp-lib](https://github.com/php-enqueue/enqueue-dev/blob/master/docs/transport/amqp_lib.md) transport which is based on [php-amqplib](https://github.com/php-amqplib/php-amqplib). -There is possibility to use any [amqp interop](https://github.com/queue-interop/queue-interop#amqp-interop) compatible transport, for example `enqueue/amqp-ext` or `enqueue/amqp-bunny`. -Here's an example on how one can change the transport to `enqueue/amqp-bunny`. - -First, install desired transport package: - -```bash -composer require enqueue/amqp-bunny:^0.8 -``` - -Change the factory class in `config/queue.php`: - -```php - // ... - 'connections' => [ - 'rabbitmq' => [ - 'driver' => 'rabbitmq', - 'factory_class' => Enqueue\AmqpBunny\AmqpConnectionFactory::class, - ], - ], -``` +## Support Policy + +| Package Version | Laravel Version | Bug Fixes Until | +|-----------------|-----------------|---------------------| +| 6.0 | 5.5 | August 30th, 2019 | +| 7.0 | 5.6 | August 7th, 2018 | +| 7.1 | 5.7 | March 4th, 2019 | +| 7.2 | 5.8 | August 26th, 2019 | +| 9 | 6 | September 3rd, 2021 | +| 10 | 6 | September 3rd, 2021 | ## Testing diff --git a/composer.json b/composer.json index 6094beb9..32f1a5e2 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "vladimir-yuldashev/laravel-queue-rabbitmq", - "description": "RabbitMQ driver for Laravel Queue. Supports Laravel Horizon", + "description": "RabbitMQ driver for Laravel Queue. Supports Laravel Horizon.", "license": "MIT", "authors": [ { @@ -14,15 +14,13 @@ "illuminate/database": "^6.0", "illuminate/support": "^6.0", "illuminate/queue": "^6.0", - "enqueue/amqp-lib": "0.9.*", - "queue-interop/amqp-interop": "0.8.*" + "php-amqplib/php-amqplib": "^2.11" }, "require-dev": { "phpunit/phpunit": "^8.4", "illuminate/events": "^6.0", "mockery/mockery": "^1.0", "laravel/horizon": "^3.0", - "larapack/dd": "^1.1", "friendsofphp/php-cs-fixer": "^2.16", "orchestra/testbench": "^4.3" }, diff --git a/config/rabbitmq.php b/config/rabbitmq.php index 02674904..cd2c008f 100644 --- a/config/rabbitmq.php +++ b/config/rabbitmq.php @@ -8,85 +8,32 @@ return [ 'driver' => 'rabbitmq', - - /* - * Set to "horizon" if you wish to use Laravel Horizon. - */ - 'worker' => env('RABBITMQ_WORKER', 'default'), - - 'dsn' => env('RABBITMQ_DSN', null), - - /* - * Could be one a class that implements \Interop\Amqp\AmqpConnectionFactory for example: - * - \EnqueueAmqpExt\AmqpConnectionFactory if you install enqueue/amqp-ext - * - \EnqueueAmqpLib\AmqpConnectionFactory if you install enqueue/amqp-lib - * - \EnqueueAmqpBunny\AmqpConnectionFactory if you install enqueue/amqp-bunny - */ - 'factory_class' => Enqueue\AmqpLib\AmqpConnectionFactory::class, - - 'host' => env('RABBITMQ_HOST', '127.0.0.1'), - 'port' => env('RABBITMQ_PORT', 5672), - - 'vhost' => env('RABBITMQ_VHOST', '/'), - 'login' => env('RABBITMQ_LOGIN', 'guest'), - 'password' => env('RABBITMQ_PASSWORD', 'guest'), - 'queue' => env('RABBITMQ_QUEUE', 'default'), - - 'options' => [ - - 'exchange' => [ - - 'name' => env('RABBITMQ_EXCHANGE_NAME'), - - /* - * Determine if exchange should be created if it does not exist. - */ - 'declare' => env('RABBITMQ_EXCHANGE_DECLARE', true), - - /* - * Read more about possible values at https://www.rabbitmq.com/tutorials/amqp-concepts.html - */ - 'type' => env('RABBITMQ_EXCHANGE_TYPE', \Interop\Amqp\AmqpTopic::TYPE_DIRECT), - 'passive' => env('RABBITMQ_EXCHANGE_PASSIVE', false), - 'durable' => env('RABBITMQ_EXCHANGE_DURABLE', true), - 'auto_delete' => env('RABBITMQ_EXCHANGE_AUTODELETE', false), - 'arguments' => env('RABBITMQ_EXCHANGE_ARGUMENTS'), + 'connection' => PhpAmqpLib\Connection\AMQPLazyConnection::class, + + 'hosts' => [ + [ + 'host' => env('RABBITMQ_HOST', '127.0.0.1'), + 'port' => env('RABBITMQ_PORT', 5672), + 'user' => env('RABBITMQ_USER', 'guest'), + 'password' => env('RABBITMQ_PASSWORD', 'guest'), + 'vhost' => env('RABBITMQ_VHOST', '/'), ], + ], - 'queue' => [ - - /* - * Determine if queue should be created if it does not exist. - */ - 'declare' => env('RABBITMQ_QUEUE_DECLARE', true), - - /* - * Determine if queue should be binded to the exchange created. - */ - 'bind' => env('RABBITMQ_QUEUE_DECLARE_BIND', true), - - /* - * Read more about possible values at https://www.rabbitmq.com/tutorials/amqp-concepts.html - */ - 'passive' => env('RABBITMQ_QUEUE_PASSIVE', false), - 'durable' => env('RABBITMQ_QUEUE_DURABLE', true), - 'exclusive' => env('RABBITMQ_QUEUE_EXCLUSIVE', false), - 'auto_delete' => env('RABBITMQ_QUEUE_AUTODELETE', false), - 'arguments' => env('RABBITMQ_QUEUE_ARGUMENTS'), + 'options' => [ + 'ssl_options' => [ + 'cafile' => env('RABBITMQ_OPTIONS_SSL_CAFILE', null), + 'local_cert' => env('RABBITMQ_OPTIONS_SSL_LOCALCERT', null), + 'local_key' => env('RABBITMQ_OPTIONS_SSL_LOCALKEY', null), + 'verify_peer' => env('RABBITMQ_OPTIONS_SSL_VERIFY_PEER', true), + 'passphrase' => env('RABBITMQ_OPTIONS_SSL_PASSPHRASE', null), ], ], /* - * Optional SSL params if an SSL connection is used + * Set to "horizon" if you wish to use Laravel Horizon. */ - 'ssl_params' => [ - 'ssl_on' => env('RABBITMQ_SSL', false), - 'cafile' => env('RABBITMQ_SSL_CAFILE', null), - 'local_cert' => env('RABBITMQ_SSL_LOCALCERT', null), - 'local_key' => env('RABBITMQ_SSL_LOCALKEY', null), - 'verify_peer' => env('RABBITMQ_SSL_VERIFY_PEER', true), - 'passphrase' => env('RABBITMQ_SSL_PASSPHRASE', null), - ], + 'worker' => env('RABBITMQ_WORKER', 'default'), ]; diff --git a/docker-compose.yml b/docker-compose.yml index 189e4ac8..4791bb01 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,19 +3,21 @@ version: '3.7' services: rabbitmq: - image: rabbitmq + image: rabbitmq:management environment: RABBITMQ_DEFAULT_USER: guest RABBITMQ_DEFAULT_PASSWORD: guest RABBITMQ_DEFAULT_VHOST: / - RABBITMQ_SSL_CACERTFILE: /rootCA.pem - RABBITMQ_SSL_CERTFILE: /rootCA.pem - RABBITMQ_SSL_KEYFILE: /rootCA.key - RABBITMQ_SSL_VERIFY: verify_none - RABBITMQ_SSL_FAIL_IF_NO_PEER_CERT: "false" + RABBITMQ_MANAGEMENT_SSL_CACERTFILE: /rootCA.pem + RABBITMQ_MANAGEMENT_SSL_CERTFILE: /rootCA.pem + RABBITMQ_MANAGEMENT_SSL_KEYFILE: /rootCA.key + RABBITMQ_MANAGEMENT_SSL_VERIFY: verify_none + RABBITMQ_MANAGEMENT_SSL_FAIL_IF_NO_PEER_CERT: "false" volumes: - "./tests/files/rootCA.pem:/rootCA.pem:ro" - "./tests/files/rootCA.key:/rootCA.key:ro" ports: + - 15671:15671 + - 15672:15672 - 5671:5671 - 5672:5672 diff --git a/phpunit.xml.dist b/phpunit.xml.dist index d31909dc..4ebd0b75 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -21,7 +21,7 @@ - + diff --git a/src/Console/ConsumeCommand.php b/src/Console/ConsumeCommand.php new file mode 100644 index 00000000..aec35f9f --- /dev/null +++ b/src/Console/ConsumeCommand.php @@ -0,0 +1,56 @@ +laravel['config']->get('queue.connections.' . $this->argument('connection')); + + $queue = $connector->connect($config); + + $channel = $queue->getChannel(); + + $channel->basic_consume( + $this->argument('queue'), + $this->consumerTag(), + $this->option('no-local'), + $this->option('no-ack'), + $this->option('exclusive'), + false, + [$this, 'processMessage'] + ); + + while($channel->is_consuming()) { + $channel->wait(); + } + } + + protected function processMessage(AMQPMessage $AMQPMessage): void + { + + } + + protected function consumerTag(): string + { + if ($this->option('consumer-tag')) { + return (string)$this->option('consumer-tag'); + } + + return config('app.name') . '_' . getmygid(); + } +} diff --git a/src/Console/ExchangeMakeCommand.php b/src/Console/ExchangeMakeCommand.php new file mode 100644 index 00000000..68cd5ad3 --- /dev/null +++ b/src/Console/ExchangeMakeCommand.php @@ -0,0 +1,62 @@ +laravel['config']->get('queue.connections.' . $this->argument('connection')); + + $queue = $connector->connect($config); + $channel = $queue->getChannel(); + + try { + $channel->exchange_declare($this->argument('name'), '', true); + } catch (AMQPProtocolChannelException $exception) { + if($exception->amqp_reply_code === 404) { + $this->declareExchange($queue->getConnection()->channel()); + + $this->info('Exchange declared successfully.'); + + return; + } + + throw $exception; + } + + $this->warn('Exchange already exists.'); + } + + protected function declareExchange(AMQPChannel $channel): void + { + $channel->exchange_declare( + $this->argument('name'), + $this->option('type'), + false, + $this->option('durable'), + $this->option('auto-delete'), + false, + true + ); + } +} diff --git a/src/Console/QueueBindCommand.php b/src/Console/QueueBindCommand.php new file mode 100644 index 00000000..56eaf507 --- /dev/null +++ b/src/Console/QueueBindCommand.php @@ -0,0 +1,36 @@ +laravel['config']->get('queue.connections.' . $this->argument('connection')); + + $queue = $connector->connect($config); + + $channel = $queue->getChannel(); + + $channel->queue_bind( + $this->argument('queue'), + $this->argument('exchange'), + (string) $this->option('routing-key') + ); + } +} diff --git a/src/Console/QueueMakeCommand.php b/src/Console/QueueMakeCommand.php new file mode 100644 index 00000000..942fb726 --- /dev/null +++ b/src/Console/QueueMakeCommand.php @@ -0,0 +1,61 @@ +laravel['config']->get('queue.connections.' . $this->argument('connection')); + + $queue = $connector->connect($config); + + $channel = $queue->getChannel(); + + try { + $channel->queue_declare($this->argument('name'), true); + } catch (AMQPProtocolChannelException $exception) { + if ($exception->amqp_reply_code === 404) { + $this->declareQueue($queue->getConnection()->channel()); + + $this->info('Queue declared successfully.'); + + return; + } + + throw $exception; + } + + $this->warn('Queue already exists.'); + } + + protected function declareQueue(AMQPChannel $channel): void + { + $channel->queue_declare( + $this->argument('name'), + false, + $this->option('durable'), + false, + $this->option('auto-delete'), + false + ); + } +} diff --git a/src/Console/QueuePurgeCommand.php b/src/Console/QueuePurgeCommand.php new file mode 100644 index 00000000..4ce09253 --- /dev/null +++ b/src/Console/QueuePurgeCommand.php @@ -0,0 +1,11 @@ +size($queue); } - /** {@inheritdoc} */ + /** + * {@inheritdoc} + */ public function push($job, $data = '', $queue = null) { $this->lastPushed = $job; @@ -40,37 +42,45 @@ public function push($job, $data = '', $queue = null) return parent::push($job, $data, $queue); } - /** {@inheritdoc} */ - public function pushRaw($payload, $queueName = null, array $options = []) + /** + * {@inheritdoc} + */ + public function pushRaw($payload, $queue = null, array $options = []) { $payload = (new JobPayload($payload))->prepare($this->lastPushed)->value; - return tap(parent::pushRaw($payload, $queueName, $options), function () use ($queueName, $payload): void { - $this->event($this->getQueueName($queueName), new JobPushed($payload)); + return tap(parent::pushRaw($payload, $queue, $options), function () use ($queue, $payload): void { + $this->event($this->getQueue($queue), new JobPushed($payload)); }); } - /** {@inheritdoc} */ + /** + * {@inheritdoc} + */ public function later($delay, $job, $data = '', $queueName = null) { $payload = (new JobPayload($this->createPayload($job, $data)))->prepare($job)->value; return tap(parent::pushRaw($payload, $queueName, ['delay' => $this->secondsUntil($delay)]), function () use ($payload, $queueName): void { - $this->event($this->getQueueName($queueName), new JobPushed($payload)); + $this->event($this->getQueue($queueName), new JobPushed($payload)); }); } - /** {@inheritdoc} */ - public function pop($queueName = null) + /** + * {@inheritdoc} + */ + public function pop($queue = null) { - return tap(parent::pop($queueName), function ($result) use ($queueName): void { + return tap(parent::pop($queue), function ($result) use ($queue): void { if ($result instanceof RabbitMQJob) { - $this->event($queueName ?: $this->queueName, new JobReserved($result->getRawBody())); + $this->event($queue ?: $this->default, new JobReserved($result->getRawBody())); } }); } - /** {@inheritdoc} */ + /** + * {@inheritdoc} + */ public function release($delay, $job, $data, $queue, $attempts = 0) { $this->lastPushed = $job; @@ -88,7 +98,7 @@ public function release($delay, $job, $data, $queue, $attempts = 0) */ public function deleteReserved($queueName, $job): void { - $this->event($this->getQueueName($queueName), new JobDeleted($job, $job->getRawBody())); + $this->event($this->getQueue($queueName), new JobDeleted($job, $job->getRawBody())); } /** @@ -108,7 +118,9 @@ protected function event($queue, $event): void } } - /** {@inheritdoc} */ + /** + * {@inheritdoc} + */ protected function getRandomId(): string { return JobId::generate(); diff --git a/src/LaravelQueueRabbitMQServiceProvider.php b/src/LaravelQueueRabbitMQServiceProvider.php index ac210ee9..1093bed2 100644 --- a/src/LaravelQueueRabbitMQServiceProvider.php +++ b/src/LaravelQueueRabbitMQServiceProvider.php @@ -4,6 +4,7 @@ use Illuminate\Queue\QueueManager; use Illuminate\Support\ServiceProvider; +use VladimirYuldashev\LaravelQueueRabbitMQ\Console; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Connectors\RabbitMQConnector; class LaravelQueueRabbitMQServiceProvider extends ServiceProvider @@ -19,6 +20,16 @@ public function register(): void __DIR__.'/../config/rabbitmq.php', 'queue.connections.rabbitmq' ); + + if ($this->app->runningInConsole()) { + $this->commands([ + Console\ConsumeCommand::class, + Console\ExchangeMakeCommand::class, + Console\QueueBindCommand::class, + Console\QueueMakeCommand::class, + Console\QueuePurgeCommand::class, + ]); + } } /** diff --git a/src/Queue/Connectors/RabbitMQConnector.php b/src/Queue/Connectors/RabbitMQConnector.php index 1998b6f2..bebe27e4 100644 --- a/src/Queue/Connectors/RabbitMQConnector.php +++ b/src/Queue/Connectors/RabbitMQConnector.php @@ -2,22 +2,16 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Connectors; -use Enqueue\AmqpLib\AmqpConnectionFactory as EnqueueAmqpConnectionFactory; -use Enqueue\AmqpTools\DelayStrategyAware; -use Enqueue\AmqpTools\RabbitMqDlxDelayStrategy; +use Exception; use Illuminate\Contracts\Events\Dispatcher; use Illuminate\Contracts\Queue\Queue; use Illuminate\Queue\Connectors\ConnectorInterface; use Illuminate\Queue\Events\JobFailed; use Illuminate\Queue\Events\WorkerStopping; use Illuminate\Support\Arr; -use Interop\Amqp\AmqpConnectionFactory; -use Interop\Amqp\AmqpConnectionFactory as InteropAmqpConnectionFactory; -use Interop\Amqp\AmqpContext; use InvalidArgumentException; -use LogicException; -use ReflectionClass; -use ReflectionException; +use PhpAmqpLib\Connection\AbstractConnection; +use PhpAmqpLib\Connection\AMQPLazyConnection; use VladimirYuldashev\LaravelQueueRabbitMQ\Horizon\Listeners\RabbitMQFailedEvent; use VladimirYuldashev\LaravelQueueRabbitMQ\Horizon\RabbitMQQueue as HorizonRabbitMQQueue; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue; @@ -39,72 +33,51 @@ public function __construct(Dispatcher $dispatcher) * * @param array $config * - * @return Queue - * @throws ReflectionException + * @return RabbitMQQueue + * @throws Exception */ public function connect(array $config): Queue { - /** @var AmqpContext $context */ - $context = self::createContext($config); + $connection = $this->createConnection($config); + $channel = $connection->channel(); - $this->dispatcher->listen(WorkerStopping::class, function () use ($context): void { - $context->close(); + $this->dispatcher->listen(WorkerStopping::class, static function () use ($channel, $connection): void { + $channel->close(); + $connection->close(); }); $worker = Arr::get($config, 'worker', 'default'); if ($worker === 'default') { - return new RabbitMQQueue($context, $config); + return new RabbitMQQueue($connection, $channel, $config['queue']); } if ($worker === 'horizon') { $this->dispatcher->listen(JobFailed::class, RabbitMQFailedEvent::class); - return new HorizonRabbitMQQueue($context, $config); + return new HorizonRabbitMQQueue($connection, $channel, $config['queue']); } if ($worker instanceof RabbitMQQueue) { - return new $worker($context, $config); + return new $worker($connection, $config); } throw new InvalidArgumentException('Invalid worker.'); } /** - * Create a context. - * * @param array $config - * @return AmqpContext - * @throws ReflectionException + * @return AbstractConnection + * @throws Exception */ - public static function createContext(array $config): AmqpContext + protected function createConnection(array $config): AbstractConnection { - $factoryClass = Arr::get($config, 'factory_class', EnqueueAmqpConnectionFactory::class); - - if (! class_exists($factoryClass) || ! (new ReflectionClass($factoryClass))->implementsInterface(InteropAmqpConnectionFactory::class)) { - throw new LogicException(sprintf('The factory_class option has to be valid class that implements "%s"', InteropAmqpConnectionFactory::class)); - } - - /** @var AmqpConnectionFactory $factory */ - $factory = new $factoryClass([ - 'dsn' => Arr::get($config, 'dsn'), - 'host' => Arr::get($config, 'host', '127.0.0.1'), - 'port' => Arr::get($config, 'port', 5672), - 'user' => Arr::get($config, 'login', 'guest'), - 'pass' => Arr::get($config, 'password', 'guest'), - 'vhost' => Arr::get($config, 'vhost', '/'), - 'ssl_on' => Arr::get($config, 'ssl_params.ssl_on', false), - 'ssl_verify' => Arr::get($config, 'ssl_params.verify_peer', true), - 'ssl_cacert' => Arr::get($config, 'ssl_params.cafile'), - 'ssl_cert' => Arr::get($config, 'ssl_params.local_cert'), - 'ssl_key' => Arr::get($config, 'ssl_params.local_key'), - 'ssl_passphrase' => Arr::get($config, 'ssl_params.passphrase'), - ]); - - if ($factory instanceof DelayStrategyAware) { - $factory->setDelayStrategy(new RabbitMqDlxDelayStrategy()); - } + /** @var AbstractConnection $connection */ + $connection = Arr::get($config, 'connection', AMQPLazyConnection::class); - return $factory->createContext(); + return $connection::create_connection( + Arr::get($config, 'hosts', []), + Arr::get($config, 'options', []) + ); } } diff --git a/src/Queue/Jobs/RabbitMQJob.php b/src/Queue/Jobs/RabbitMQJob.php index 0312a921..9cc90937 100644 --- a/src/Queue/Jobs/RabbitMQJob.php +++ b/src/Queue/Jobs/RabbitMQJob.php @@ -2,183 +2,138 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Jobs; -use Exception; -use Illuminate\Container\Container; +use Illuminate\Contracts\Container\BindingResolutionException; use Illuminate\Contracts\Queue\Job as JobContract; -use Illuminate\Database\DetectsLostConnections; use Illuminate\Queue\Jobs\Job; -use Illuminate\Queue\Jobs\JobName; -use Illuminate\Support\Str; -use Interop\Amqp\AmqpConsumer; -use Interop\Amqp\AmqpMessage; -use VladimirYuldashev\LaravelQueueRabbitMQ\Horizon\RabbitMQQueue as HorizonRabbitMQQueue; +use Illuminate\Support\Arr; +use PhpAmqpLib\Message\AMQPMessage; +use PhpAmqpLib\Wire\AMQPTable; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue; +use VladimirYuldashev\LaravelQueueRabbitMQ\Horizon\RabbitMQQueue as HorizonRabbitMQQueue; class RabbitMQJob extends Job implements JobContract { - use DetectsLostConnections; - /** - * Same as RabbitMQQueue, used for attempt counts. + * The RabbitMQ queue instance. + * + * @var RabbitMQQueue */ - public const ATTEMPT_COUNT_HEADERS_KEY = 'attempts_count'; + protected $rabbitmq; - protected $connection; - protected $consumer; + /** + * The RabbitMQ message instance. + * + * @var AMQPMessage + */ protected $message; + /** + * The JSON decoded version of "$message". + * + * @var array + */ + protected $decoded; + public function __construct( - Container $container, - RabbitMQQueue $connection, - AmqpConsumer $consumer, - AmqpMessage $message + RabbitMQQueue $rabbitmq, + AMQPMessage $message, + string $queue ) { - $this->container = $container; - $this->connection = $connection; - $this->consumer = $consumer; + $this->rabbitmq = $rabbitmq; $this->message = $message; - $this->queue = $consumer->getQueue()->getQueueName(); - $this->connectionName = $connection->getConnectionName(); + $this->queue = $queue; + $this->decoded = $this->payload(); } + /** - * Fire the job. - * - * @throws Exception - * - * @return void + * {@inheritdoc} */ - public function fire(): void + public function getJobId() { - try { - $payload = $this->payload(); - - [$class, $method] = JobName::parse($payload['job']); - - with($this->instance = $this->resolve($class))->{$method}($this, $payload['data']); - } catch (Exception $exception) { - if ( - $this->causedByLostConnection($exception) || - Str::contains($exception->getMessage(), ['detected deadlock']) - ) { - sleep(2); - $this->fire(); - - return; - } + return json_decode($this->message->getBody(), true)['id'] ?? null; + } - throw $exception; - } + /** + * {@inheritdoc} + */ + public function getRawBody(): string + { + return $this->message->getBody(); } /** - * Get the number of times the job has been attempted. - * - * @return int + * {@inheritdoc} */ public function attempts(): int { - // set default job attempts to 1 so that jobs can run without retry - $defaultAttempts = 1; + /** @var AMQPTable|null $headers */ + $headers = Arr::get($this->message->get_properties(), 'application_headers'); + + if (!$headers) { + return 0; + } + + $data = $headers->getNativeData(); + + $laravelAttempts = Arr::get($data, 'laravel.attempts', 0); + $xDeathCount = Arr::get($headers->getNativeData(), 'x-death.0.count', 0); - return $this->message->getProperty(self::ATTEMPT_COUNT_HEADERS_KEY, $defaultAttempts); + return $laravelAttempts + $xDeathCount; } /** - * Get the raw body string for the job. + * {@inheritdoc} * - * @return string + * @throws BindingResolutionException */ - public function getRawBody(): string - { - return $this->message->getBody(); - } - - /** {@inheritdoc} */ public function delete(): void { parent::delete(); - $this->consumer->acknowledge($this->message); + $this->rabbitmq->ack($this); // required for Laravel Horizon - if ($this->connection instanceof HorizonRabbitMQQueue) { - $this->connection->deleteReserved($this->queue, $this); + if ($this->rabbitmq instanceof HorizonRabbitMQQueue) { + $this->rabbitmq->deleteReserved($this->queue, $this); } } - /** {@inheritdoc} - * @throws Exception + /** + * {@inheritdoc} */ public function release($delay = 0): void { parent::release($delay); - $this->delete(); + if ($delay > 0) { + $this->rabbitmq->ack($this); - $body = $this->payload(); + $this->rabbitmq->laterRaw($delay, $this->message->body, $this->queue, $this->attempts()); - /* - * Some jobs don't have the command set, so fall back to just sending it the job name string - */ - if (isset($body['data']['command']) === true) { - $job = $this->unserialize($body); - } else { - $job = $this->getName(); + return; } - $data = $body['data']; - - $this->connection->release($delay, $job, $data, $this->getQueue(), $this->attempts() + 1); + $this->rabbitmq->reject($this); } /** - * Get the job identifier. + * Get the underlying RabbitMQ connection. * - * @return string|null + * @return RabbitMQQueue */ - public function getJobId(): ?string + public function getRabbitMQ(): RabbitMQQueue { - return $this->message->getCorrelationId(); + return $this->rabbitmq; } /** - * Sets the job identifier. - * - * @param string $id + * Get the underlying RabbitMQ message. * - * @return void + * @return AMQPMessage */ - public function setJobId($id): void + public function getRabbitMQMessage(): AMQPMessage { - $this->connection->setCorrelationId($id); - } - - /** - * Unserialize job. - * - * @param array $body - * - * @throws Exception - * - * @return mixed - */ - protected function unserialize(array $body) - { - try { - /* @noinspection UnserializeExploitsInspection */ - return unserialize($body['data']['command']); - } catch (Exception $exception) { - if ( - $this->causedByLostConnection($exception) || - Str::contains($exception->getMessage(), ['detected deadlock']) - ) { - sleep(2); - - return $this->unserialize($body); - } - - throw $exception; - } + return $this->message; } } diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index 5699d102..47cae98f 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -2,65 +2,85 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Queue; -use DateInterval; -use DateTimeInterface; +use Exception; use Illuminate\Contracts\Queue\Queue as QueueContract; use Illuminate\Queue\Queue; use Illuminate\Support\Str; -use Interop\Amqp\AmqpContext; -use Interop\Amqp\AmqpMessage; -use Interop\Amqp\AmqpQueue; -use Interop\Amqp\AmqpTopic; -use Interop\Amqp\Impl\AmqpBind; -use Interop\Queue\Exception; +use PhpAmqpLib\Channel\AMQPChannel; +use PhpAmqpLib\Connection\AbstractConnection; +use PhpAmqpLib\Exception\AMQPProtocolChannelException; +use PhpAmqpLib\Exchange\AMQPExchangeType; +use PhpAmqpLib\Message\AMQPMessage; +use PhpAmqpLib\Wire\AMQPTable; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Jobs\RabbitMQJob; class RabbitMQQueue extends Queue implements QueueContract { - protected $queueName; - protected $queueOptions; - protected $exchangeOptions; - - protected $declaredExchanges = []; - protected $declaredQueues = []; + /** + * The RabbitMQ connection instance. + * + * @var AMQPChannel + */ + protected $connection; /** - * @var AmqpContext + * The RabbitMQ channel instance. + * + * @var AMQPChannel */ - protected $context; - protected $config; - protected $correlationId; + protected $channel; - public function __construct(AmqpContext $context, array $config) - { - $this->config = $config; - $this->context = $context; + /** + * The name of the default queue. + * + * @var string + */ + protected $default; - $this->queueName = $config['queue'] ?? $config['options']['queue']['name']; - $this->queueOptions = $config['options']['queue']; - $this->queueOptions['arguments'] = isset($this->queueOptions['arguments']) ? - json_decode($this->queueOptions['arguments'], true) : []; + /** + * List of already declared queues. + * + * @var array + */ + protected $queues; - $this->exchangeOptions = $config['options']['exchange']; - $this->exchangeOptions['arguments'] = isset($this->exchangeOptions['arguments']) ? - json_decode($this->exchangeOptions['arguments'], true) : []; + public function __construct( + AbstractConnection $connection, + AMQPChannel $channel, + string $default + ) + { + $this->connection = $connection; + $this->channel = $channel; + $this->default = $default; } /** * {@inheritdoc} */ - public function size($queueName = null): int + public function size($queue = null): int { - /** @var AmqpQueue $queue */ - [$queue] = $this->declareEverything($queueName); - - return $this->context->declareQueue($queue); + // TODO count delayed too + $queue = $this->getQueue($queue); + + [, $size] = $this->channel->queue_declare( + $queue, + false, + true, + false, + false, + false, + new AMQPTable([ + 'x-dead-letter-exchange' => $queue, + 'x-dead-letter-routing-key' => $queue, + ]) + ); + + return $size; } /** * {@inheritdoc} - * - * @throws Exception */ public function push($job, $data = '', $queue = null) { @@ -69,188 +89,222 @@ public function push($job, $data = '', $queue = null) /** * {@inheritdoc} - * - * @throws Exception */ - public function pushRaw($payload, $queueName = null, array $options = []) + public function pushRaw($payload, $queue = null, array $options = []) { - /** - * @var AmqpTopic - * @var AmqpQueue $queue - */ - [$queue, $topic] = $this->declareEverything($queueName); - - /** @var AmqpMessage $message */ - $message = $this->context->createMessage($payload); - - $message->setCorrelationId($this->getCorrelationId()); - $message->setContentType('application/json'); - $message->setDeliveryMode(AmqpMessage::DELIVERY_MODE_PERSISTENT); - - if (isset($options['routing_key'])) { - $message->setRoutingKey($options['routing_key']); - } else { - $message->setRoutingKey($queue->getQueueName()); + $queue = $this->getQueue($queue); + + if (!isset($this->queues[$queue])) { + $this->channel->exchange_declare( + $queue, + AMQPExchangeType::DIRECT, + false, + true, + true + ); + + $this->channel->queue_declare( + $queue, + false, + true, + false, + false, + false, + new AMQPTable([ + 'x-dead-letter-exchange' => $queue, + 'x-dead-letter-routing-key' => $queue, + ]) + ); + + $this->channel->queue_bind($queue, $queue, $queue); } - if (isset($options['priority'])) { - $message->setPriority($options['priority']); - } - - if (isset($options['expiration'])) { - $message->setExpiration($options['expiration']); - } - - if (isset($options['headers'])) { - $message->setHeaders($options['headers']); - } - - if (isset($options['properties'])) { - $message->setProperties($options['properties']); - } - - if (isset($options['attempts'])) { - $message->setProperty(RabbitMQJob::ATTEMPT_COUNT_HEADERS_KEY, $options['attempts']); - } + [$message, $correlationId] = $this->createMessage($payload); - $producer = $this->context->createProducer(); - if (isset($options['delay']) && $options['delay'] > 0) { - $producer->setDeliveryDelay($options['delay'] * 1000); - } - - $producer->send($topic, $message); + $this->channel->basic_publish($message, $queue, $queue, true, false); + $this->channel->wait_for_pending_acks(); - return $message->getCorrelationId(); + return $correlationId; } /** * {@inheritdoc} - * @throws Exception */ public function later($delay, $job, $data = '', $queue = null) { - return $this->pushRaw($this->createPayload($job, $queue, $data), $queue, ['delay' => $this->secondsUntil($delay)]); + return $this->laterRaw( + $delay, + $this->createPayload($job, $queue, $data), + $queue + ); } - /** - * {@inheritDoc} - * @throws Exception - */ - public function release($delay, $job, $data, $queue, $attempts = 0) + public function laterRaw($delay, $payload, $queue = null, $attempts = 0) { - return $this->pushRaw($this->createPayload($job, $queue, $data), $queue, [ - 'delay' => $this->secondsUntil($delay), - 'attempts' => $attempts, - ]); + $ttl = $this->secondsUntil($delay) * 1000; + + $destinationQueue = $this->getQueue($queue); + $delayedQueue = $this->getQueue($queue).'.delay.'.$ttl; + + $this->channel->exchange_declare( + $destinationQueue, + AMQPExchangeType::DIRECT, + false, + true, + true + ); + + $this->channel->queue_declare( + $delayedQueue, + false, + true, + false, + false, + false, + new AMQPTable([ + 'x-dead-letter-exchange' => $destinationQueue, + 'x-dead-letter-routing-key' => $destinationQueue, + 'x-message-ttl' => $ttl, // TODO + ]) + ); + + $this->channel->queue_bind($destinationQueue, $destinationQueue, $destinationQueue); + + [$message, $correlationId] = $this->createMessage($payload, $attempts); + + $this->channel->basic_publish($message, null, $delayedQueue, true, false); + $this->channel->wait_for_pending_acks(); + + return $correlationId; } /** * {@inheritdoc} */ - public function pop($queueName = null) + public function bulk($jobs, $data = '', $queue = null): void { - /** @var AmqpQueue $queue */ - [$queue] = $this->declareEverything($queueName); - - $consumer = $this->context->createConsumer($queue); - - if ($message = $consumer->receiveNoWait()) { - return new RabbitMQJob($this->container, $this, $consumer, $message); + $queue = $this->getQueue($queue); + + foreach ((array)$jobs as $job) { + [$message] = $this->createMessage( + $this->createPayload($job, $queue, $data) + ); + + if (!isset($this->queues[$queue])) { + $this->channel->exchange_declare( + $queue, + AMQPExchangeType::DIRECT, + false, + true, + true + ); + + $this->channel->queue_declare( + $queue, + false, + true, + false, + false, + false, + new AMQPTable([ + 'x-dead-letter-exchange' => $queue, + 'x-dead-letter-routing-key' => $queue, + ]) + ); + + $this->channel->queue_bind($queue, $queue, $queue); + } + + $this->channel->batch_basic_publish($message); } - return null; + $this->channel->publish_batch(); } /** - * Retrieves the correlation id, or a unique id. + * {@inheritdoc} * - * @return string + * @throws Exception */ - public function getCorrelationId(): string + public function pop($queue = null) { - return $this->correlationId ?: uniqid('', true); + try { + $queue = $this->getQueue($queue); + + /** @var AMQPMessage|null $message */ + if ($message = $this->channel->basic_get($queue)) { + return new RabbitMQJob($this, $message, $queue); + } + } catch (AMQPProtocolChannelException $exception) { + // if there is not exchange or queue AMQP will throw exception with code 404 + // we need to catch it and return null + if ($exception->amqp_reply_code === 404) { + return null; + } + + throw $exception; + } + + return null; } - /** - * Sets the correlation id for a message to be published. - * - * @param string $id - * - * @return void - */ - public function setCorrelationId(string $id): void + public function getConnection(): AbstractConnection { - $this->correlationId = $id; + return $this->connection; } - /** - * @return AmqpContext - */ - public function getContext(): AmqpContext + public function getChannel(): AMQPChannel { - return $this->context; + return $this->channel; } - /** - * @param string $queueName - * - * @return array [Interop\Amqp\AmqpQueue, Interop\Amqp\AmqpTopic] - */ - public function declareEverything(string $queueName = null): array + public function getQueue($queue = null) { - $queueName = $this->getQueueName($queueName); - $exchangeName = $this->exchangeOptions['name'] ?: $queueName; - - $topic = $this->context->createTopic($exchangeName); - $topic->setType($this->exchangeOptions['type']); - $topic->setArguments($this->exchangeOptions['arguments']); - if ($this->exchangeOptions['passive']) { - $topic->addFlag(AmqpTopic::FLAG_PASSIVE); - } - if ($this->exchangeOptions['durable']) { - $topic->addFlag(AmqpTopic::FLAG_DURABLE); - } - if ($this->exchangeOptions['auto_delete']) { - $topic->addFlag(AmqpTopic::FLAG_AUTODELETE); - } - - if ($this->exchangeOptions['declare'] && !in_array($exchangeName, $this->declaredExchanges, true)) { - $this->context->declareTopic($topic); + return $queue ?: $this->default; + } - $this->declaredExchanges[] = $exchangeName; - } + public function purge($queue = null): void + { + $this->channel->queue_purge($this->getQueue($queue)); + $this->channel->wait_for_pending_acks(); + } - $queue = $this->context->createQueue($queueName); - $queue->setArguments($this->queueOptions['arguments']); - if ($this->queueOptions['passive']) { - $queue->addFlag(AmqpQueue::FLAG_PASSIVE); - } - if ($this->queueOptions['durable']) { - $queue->addFlag(AmqpQueue::FLAG_DURABLE); - } - if ($this->queueOptions['exclusive']) { - $queue->addFlag(AmqpQueue::FLAG_EXCLUSIVE); - } - if ($this->queueOptions['auto_delete']) { - $queue->addFlag(AmqpQueue::FLAG_AUTODELETE); - } + public function ack(RabbitMQJob $job): void + { + $this->channel->basic_ack($job->getRabbitMQMessage()->getDeliveryTag()); + $this->channel->wait_for_pending_acks(); + } - if ($this->queueOptions['declare'] && !in_array($queueName, $this->declaredQueues, true)) { - $this->context->declareQueue($queue); + public function reject(RabbitMQJob $job): void + { + $this->channel->basic_reject($job->getRabbitMQMessage()->getDeliveryTag(), false); + $this->channel->wait_for_pending_acks(); + } - $this->declaredQueues[] = $queueName; + protected function createMessage($payload, int $attempts = 0): array + { + $properties = [ + 'content_type' => 'application/json', + 'delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT, + ]; + + // todo + if ($correlationId = json_decode($payload, true)['id'] ?? null) { + $properties['correlation_id'] = $correlationId; } - if ($this->queueOptions['bind']) { - $this->context->bind(new AmqpBind($queue, $topic, $queue->getQueueName())); - } + $message = new AMQPMessage($payload, $properties); - return [$queue, $topic]; - } + $message->set('application_headers', new AMQPTable([ + 'laravel' => [ + 'attempts' => $attempts, + ], + ])); - protected function getQueueName($queueName = null) - { - return $queueName ?: $this->queueName; + return [ + $message, + $correlationId, + ]; } protected function createPayloadArray($job, $queue, $data = '') diff --git a/tests/Feature/QueueTest.php b/tests/Feature/QueueTest.php index 9ff21d0a..15b8acbb 100644 --- a/tests/Feature/QueueTest.php +++ b/tests/Feature/QueueTest.php @@ -2,18 +2,12 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Feature; -use Enqueue\AmqpLib\AmqpContext; use PhpAmqpLib\Connection\AMQPLazyConnection; class QueueTest extends TestCase { public function testConnection(): void { - /** @var AmqpContext $context */ - $context = $this->connection()->getContext(); - - $this->assertInstanceOf(AmqpContext::class, $context); - $this->assertInstanceOf(AMQPLazyConnection::class, $context->getLibChannel()->getConnection()); - $this->assertTrue($context->getLibChannel()->getConnection()->isConnected()); + $this->assertInstanceOf(AMQPLazyConnection::class, $this->connection()->getChannel()->getConnection()); } } diff --git a/tests/Feature/SslQueueTest.php b/tests/Feature/SslQueueTest.php index 54e83c7f..7cc01f5a 100644 --- a/tests/Feature/SslQueueTest.php +++ b/tests/Feature/SslQueueTest.php @@ -2,7 +2,7 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Feature; -use Enqueue\AmqpLib\AmqpContext; +use PhpAmqpLib\Connection\AMQPLazyConnection; use PhpAmqpLib\Connection\AMQPSSLConnection; /** @@ -12,26 +12,39 @@ class SslQueueTest extends TestCase { protected function getEnvironmentSetUp($app): void { - parent::getEnvironmentSetUp($app); - - $app['config']->set('queue.connections.rabbitmq.port', getenv('PORT_SSL')); - $app['config']->set('queue.connections.rabbitmq.ssl_params', [ - 'ssl_on' => true, - 'cafile' => getenv('RABBITMQ_SSL_CAFILE'), - 'local_cert' => null, - 'local_key' => null, - 'verify_peer' => false, - 'passphrase' => null, + $app['config']->set('queue.default', 'rabbitmq'); + $app['config']->set('queue.connections.rabbitmq', [ + 'driver' => 'rabbitmq', + 'queue' => 'default', + 'connection' => AMQPSSLConnection::class, + + 'hosts' => [ + [ + 'host' => getenv('HOST'), + 'port' => getenv('PORT_SSL'), + 'vhost' => '/', + 'user' => 'guest', + 'password' => 'guest', + ], + ], + + 'options' => [ + 'ssl_options' => [ + 'cafile' => getenv('RABBITMQ_OPTIONS_SSL_CAFILE'), + 'local_cert' => null, + 'local_key' => null, + 'verify_peer' => true, + 'passphrase' => null, + ], + ], + + 'worker' => 'default', + ]); } public function testConnection(): void { - /** @var AmqpContext $context */ - $context = $this->connection()->getContext(); - - $this->assertInstanceOf(AmqpContext::class, $context); - $this->assertInstanceOf(AMQPSSLConnection::class, $context->getLibChannel()->getConnection()); - $this->assertTrue($context->getLibChannel()->getConnection()->isConnected()); + $this->assertInstanceOf(AMQPSSLConnection::class, $this->connection()->getChannel()->getConnection()); } } diff --git a/tests/Feature/TestCase.php b/tests/Feature/TestCase.php index 2a01e6b5..75331305 100644 --- a/tests/Feature/TestCase.php +++ b/tests/Feature/TestCase.php @@ -4,45 +4,102 @@ use Illuminate\Support\Facades\Queue; use Illuminate\Support\Str; -use Interop\Queue\Exception\PurgeQueueNotSupportedException; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Jobs\RabbitMQJob; +use VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Mocks\TestJob; use VladimirYuldashev\LaravelQueueRabbitMQ\Tests\TestCase as BaseTestCase; +// https://github.com/laravel/framework/blob/6.x/tests/Queue/RedisQueueIntegrationTest.php abstract class TestCase extends BaseTestCase { - /** - * @throws PurgeQueueNotSupportedException - */ protected function setUp(): void { parent::setUp(); - $this->connection()->declareEverything('default'); + Queue::size(); - $this->connection()->getContext()->purgeQueue( - $this->connection()->getContext()->createQueue('default') - ); + $this->connection()->purge(); } - public function testPush(): void + public function testPop(): void + { + $this->assertNull(Queue::pop('foo')); + } + + public function testPushRaw(): void { $this->assertSame(0, Queue::size()); Queue::pushRaw($payload = Str::random()); - $this->assertSame(1, Queue::size()); + sleep(1); + $this->assertSame(1, Queue::size()); $this->assertNotNull($job = Queue::pop()); - + $this->assertSame(0, $job->attempts()); $this->assertInstanceOf(RabbitMQJob::class, $job); $this->assertSame($payload, $job->getRawBody()); + $this->assertNull($job->getJobId()); + $job->delete(); $this->assertSame(0, Queue::size()); } - public function testLater(): void + public function testReleaseRaw(): void + { + Queue::pushRaw($payload = Str::random()); + + sleep(1); + + $this->assertSame(1, Queue::size()); + $this->assertNotNull($job = Queue::pop()); + $this->assertSame(0, $job->attempts()); + + for ($attempt = 1; $attempt <= 3; $attempt++) { + $job->release(); + + sleep(1); // TODO ??? + + $this->assertSame(1, Queue::size()); + + $job = Queue::pop(); + + $this->assertSame($attempt, $job->attempts()); + } + } + + public function testReleaseWithDelayRaw(): void + { + $this->assertSame(0, Queue::size()); + + Queue::pushRaw($payload = Str::random()); + + sleep(1); + + $this->assertSame(1, Queue::size()); + $this->assertNotNull($job = Queue::pop()); + $this->assertSame(0, $job->attempts()); + + for ($attempt = 1; $attempt <= 3; $attempt++) { + $job->release(4); + + sleep(1); // TODO ??? + + $this->assertSame(0, Queue::size()); + $this->assertNull(Queue::pop()); + + sleep(4); + + $this->assertSame(1, Queue::size()); + + $job = Queue::pop(); + + $this->assertSame($attempt, $job->attempts()); + } + } + + public function testLaterRaw(): void { $this->assertSame(0, Queue::size()); @@ -75,20 +132,145 @@ public function testLater(): void $this->assertSame(0, Queue::size()); } - public function testJobAttempts(): void + public function testPush(): void { - $expectedPayload = __METHOD__.microtime(true); + Queue::push(new TestJob()); - Queue::pushRaw($expectedPayload); + sleep(1); - $job = Queue::pop(); - $this->assertSame(1, $job->attempts()); + $this->assertSame(1, Queue::size()); + $this->assertNotNull($job = Queue::pop()); + $this->assertSame(0, $job->attempts()); + $this->assertInstanceOf(RabbitMQJob::class, $job); + $this->assertSame(TestJob::class, $job->resolveName()); + $this->assertNotNull($job->getJobId()); - $job->release(); + $payload = $job->payload(); + + $this->assertSame(TestJob::class, $payload['displayName']); + $this->assertSame('Illuminate\Queue\CallQueuedHandler@call', $payload['job']); + $this->assertNull($payload['maxTries']); + $this->assertNull($payload['delay']); + $this->assertNull($payload['timeout']); + $this->assertNull($payload['timeoutAt']); + $this->assertSame($job->getJobId(), $payload['id']); + } - $job = Queue::pop(); + public function testRelease(): void + { + Queue::push(new TestJob()); + + sleep(1); + + $this->assertSame(1, Queue::size()); + $this->assertNotNull($job = Queue::pop()); + $this->assertSame(0, $job->attempts()); + + for ($attempt = 1; $attempt <= 3; $attempt++) { + $job->release(); + + sleep(1); // TODO ??? + + $this->assertSame(1, Queue::size()); + + $job = Queue::pop(); + + $this->assertSame($attempt, $job->attempts()); + } + } + + public function testLater(): void + { + $this->assertSame(0, Queue::size()); + + Queue::later(3, new TestJob()); + + sleep(1); + + $this->assertSame(0, Queue::size()); + $this->assertNull(Queue::pop()); + + sleep(3); + + $this->assertSame(1, Queue::size()); + $this->assertNotNull($job = Queue::pop()); $this->assertInstanceOf(RabbitMQJob::class, $job); + + $body = json_decode($job->getRawBody(), true); + + $this->assertSame(TestJob::class, $body['displayName']); + $this->assertSame('Illuminate\Queue\CallQueuedHandler@call', $body['job']); + $this->assertSame(TestJob::class, $body['data']['commandName']); + $this->assertNotNull($job->getJobId()); + + $job->delete(); + + $this->assertSame(0, Queue::size()); + } + + public function testReleaseAndReleaseWithDelayAttempts(): void + { + $this->assertSame(0, Queue::size()); + + Queue::push(new TestJob()); + + sleep(1); + + $this->assertSame(1, Queue::size()); + $this->assertNotNull($job = Queue::pop()); + + $job->release(); + + sleep(1); + + $this->assertNotNull($job = Queue::pop()); + $this->assertSame(1, $job->attempts()); + + $job->release(3); + + sleep(4); + + $this->assertNotNull($job = Queue::pop()); $this->assertSame(2, $job->attempts()); } + +// public function testFoo() +// { +// /** @var RabbitMQJob $job */ +// Queue::pushRaw('foo'); +// +// $job = Queue::pop(); +// +// $this->assertNotNull($job); +// +// $job->release(); +// +// sleep(1); +// +// $job = Queue::pop(); +// +// $this->assertNotNull($job); +// +// $this->connection()->getChannel()->basic_reject($job->getMessage()->getDeliveryTag(), false); +// $this->connection()->getChannel()->wait_for_pending_acks_returns(); +// +// sleep(1); +// +// $job = Queue::pop(); +// +// sleep(1); +// +// $this->connection()->getChannel()->basic_nack($job->getMessage()->getDeliveryTag(), false, false); +// $this->connection()->getChannel()->wait_for_pending_acks_returns(); +// +// sleep(1); +// +// $job = Queue::pop(); +// +// /** @var AMQPTable|null $headers */ +// $headers = Arr::get($job->getMessage()->get_properties(), 'application_headers'); +// +// dd($headers->getNativeData()); +// } } diff --git a/tests/Mocks/AmqpConnectionFactorySpy.php b/tests/Mocks/AmqpConnectionFactorySpy.php deleted file mode 100644 index dbb16447..00000000 --- a/tests/Mocks/AmqpConnectionFactorySpy.php +++ /dev/null @@ -1,25 +0,0 @@ -set('queue.default', 'rabbitmq'); $app['config']->set('queue.connections.rabbitmq', [ 'driver' => 'rabbitmq', - 'worker' => 'default', - 'dsn' => null, - 'factory_class' => AmqpConnectionFactory::class, - 'host' => getenv('HOST'), - 'port' => getenv('PORT'), - 'vhost' => '/', - 'login' => 'guest', - 'password' => 'guest', 'queue' => 'default', - - 'options' => [ - 'exchange' => [ - 'name' => null, - 'declare' => true, - 'type' => AmqpTopic::TYPE_DIRECT, - 'passive' => false, - 'durable' => true, - 'auto_delete' => false, - 'arguments' => '[]', + 'connection' => AMQPLazyConnection::class, + + 'hosts' => [ + [ + 'host' => getenv('HOST'), + 'port' => getenv('PORT'), + 'vhost' => '/', + 'user' => 'guest', + 'password' => 'guest', ], + ], - 'queue' => [ - 'declare' => true, - 'bind' => true, - 'passive' => false, - 'durable' => true, - 'exclusive' => false, - 'auto_delete' => false, - 'arguments' => '[]', + 'options' => [ + 'ssl_options' => [ + 'cafile' => null, + 'local_cert' => null, + 'local_key' => null, + 'verify_peer' => true, + 'passphrase' => null, ], ], - 'ssl_params' => [ - 'ssl_on' => false, - 'cafile' => null, - 'local_cert' => null, - 'local_key' => null, - 'verify_peer' => true, - 'passphrase' => null, - ], + 'worker' => 'default', + ]); } diff --git a/tests/Unit/Queue/Connectors/RabbitMQConnectorTest.php b/tests/Unit/Queue/Connectors/RabbitMQConnectorTest.php deleted file mode 100644 index 35c94a55..00000000 --- a/tests/Unit/Queue/Connectors/RabbitMQConnectorTest.php +++ /dev/null @@ -1,209 +0,0 @@ -assertTrue($rc->implementsInterface(ConnectorInterface::class)); - } - - public function testCouldBeConstructedWithDispatcherAsFirstArgument(): void - { - new RabbitMQConnector($this->createMock(Dispatcher::class)); - } - - /** - * @throws ReflectionException - */ - public function testThrowsIfFactoryClassIsNotValidClass(): void - { - $this->expectException(LogicException::class); - $this->expectExceptionMessage('The factory_class option has to be valid class that implements "Interop\Amqp\AmqpConnectionFactory"'); - - $connector = new RabbitMQConnector($this->createMock(Dispatcher::class)); - - $connector->connect(['factory_class' => 'invalidClassName']); - } - - /** - * @throws ReflectionException - */ - public function testThrowsIfFactoryClassDoesNotImplementConnectorFactoryInterface(): void - { - $this->expectException(LogicException::class); - $this->expectExceptionMessage('The factory_class option has to be valid class that implements "Interop\Amqp\AmqpConnectionFactory"'); - - $connector = new RabbitMQConnector($this->createMock(Dispatcher::class)); - - $connector->connect(['factory_class' => stdClass::class]); - } - - /** - * @throws ReflectionException - */ - public function testShouldPassExpectedConfigToConnectionFactory(): void - { - $called = false; - - AmqpConnectionFactorySpy::$spy = function ($config) use (&$called): void { - $called = true; - - $this->assertEquals([ - 'dsn' => 'theDsn', - 'host' => 'theHost', - 'port' => 'thePort', - 'user' => 'theLogin', - 'pass' => 'thePassword', - 'vhost' => 'theVhost', - 'ssl_on' => 'theSslOn', - 'ssl_verify' => 'theVerifyPeer', - 'ssl_cacert' => 'theCafile', - 'ssl_cert' => 'theLocalCert', - 'ssl_key' => 'theLocalKey', - 'ssl_passphrase' => 'thePassPhrase', - ], $config); - }; - - $connector = new RabbitMQConnector($this->createMock(Dispatcher::class)); - - $config = $this->createDummyConfig(); - $config['factory_class'] = AmqpConnectionFactorySpy::class; - - $connector->connect($config); - - $this->assertTrue($called); - } - - /** - * @throws ReflectionException - */ - public function testShouldReturnExpectedInstanceOfQueueOnConnect(): void - { - $connector = new RabbitMQConnector($this->createMock(Dispatcher::class)); - - $config = $this->createDummyConfig(); - $config['factory_class'] = AmqpConnectionFactorySpy::class; - - $queue = $connector->connect($config); - - $this->assertInstanceOf(RabbitMQQueue::class, $queue); - } - - /** - * @throws ReflectionException - */ - public function testShouldSetRabbitMqDlxDelayStrategyIfConnectionFactoryImplementsDelayStrategyAwareInterface(): void - { - $connector = new RabbitMQConnector($this->createMock(Dispatcher::class)); - - $called = false; - DelayStrategyAwareAmqpConnectionFactorySpy::$spy = function ($actualStrategy) use (&$called): void { - $this->assertInstanceOf(RabbitMqDlxDelayStrategy::class, $actualStrategy); - - $called = true; - }; - - $config = $this->createDummyConfig(); - $config['factory_class'] = DelayStrategyAwareAmqpConnectionFactorySpy::class; - - $connector->connect($config); - - $this->assertTrue($called); - } - - /** - * @throws ReflectionException - */ - public function testShouldCallContextCloseMethodOnWorkerStoppingEvent(): void - { - $contextMock = $this->createMock(AmqpContext::class); - $contextMock - ->expects($this->once()) - ->method('close'); - - $dispatcherMock = $this->createMock(Dispatcher::class); - $dispatcherMock - ->expects($this->once()) - ->method('listen') - ->with(WorkerStopping::class, $this->isInstanceOf(Closure::class)) - ->willReturnCallback(static function ($eventName, Closure $listener): void { - $listener(); - }); - - CustomContextAmqpConnectionFactoryMock::$context = $contextMock; - - $connector = new RabbitMQConnector($dispatcherMock); - - $config = $this->createDummyConfig(); - $config['factory_class'] = CustomContextAmqpConnectionFactoryMock::class; - - $connector->connect($config); - } - - /** - * @return array - */ - private function createDummyConfig(): array - { - return [ - 'dsn' => 'theDsn', - 'host' => 'theHost', - 'port' => 'thePort', - 'login' => 'theLogin', - 'password' => 'thePassword', - 'vhost' => 'theVhost', - 'ssl_params' => [ - 'ssl_on' => 'theSslOn', - 'verify_peer' => 'theVerifyPeer', - 'cafile' => 'theCafile', - 'local_cert' => 'theLocalCert', - 'local_key' => 'theLocalKey', - 'passphrase' => 'thePassPhrase', - ], - 'options' => [ - 'exchange' => [ - 'name' => 'anExchangeName', - 'declare' => false, - 'type' => AmqpTopic::TYPE_DIRECT, - 'passive' => false, - 'durable' => true, - 'auto_delete' => false, - ], - - 'queue' => [ - 'name' => 'aQueueName', - 'declare' => false, - 'bind' => false, - 'passive' => false, - 'durable' => true, - 'exclusive' => false, - 'auto_delete' => false, - 'arguments' => '[]', - ], - ], - 'sleep_on_error' => getenv('RABBITMQ_ERROR_SLEEP', 5), - ]; - } -} diff --git a/tests/Unit/Queue/Jobs/RabbitMQJobTest.php b/tests/Unit/Queue/Jobs/RabbitMQJobTest.php deleted file mode 100644 index f03a8645..00000000 --- a/tests/Unit/Queue/Jobs/RabbitMQJobTest.php +++ /dev/null @@ -1,86 +0,0 @@ -assertTrue($rc->implementsInterface(JobContract::class)); - } - - public function testShouldBeSubClassOfQueue(): void - { - $rc = new ReflectionClass(RabbitMQJob::class); - - $this->assertTrue($rc->isSubclassOf(Job::class)); - } - - public function testShouldUseDetectDeadlocksTrait(): void - { - $rc = new ReflectionClass(RabbitMQJob::class); - - $this->assertContains(DetectsLostConnections::class, $rc->getTraitNames()); - } - - public function testCouldBeConstructedWithExpectedArguments(): void - { - $queue = $this->createMock(AmqpQueue::class); - $queue - ->expects($this->once()) - ->method('getQueueName') - ->willReturn('theQueueName'); - - $consumerMock = $this->createConsumerMock(); - $consumerMock - ->expects($this->once()) - ->method('getQueue') - ->willReturn($queue); - - $connectionMock = $this->createRabbitMQQueueMock(); - $connectionMock - ->method('getConnectionName') - ->willReturn('theConnectionName'); - - $job = new RabbitMQJob( - new Container(), - $connectionMock, - $consumerMock, - new AmqpMessage() - ); - - $this->assertSame('theQueueName', $job->getQueue()); - $this->assertSame('theConnectionName', $job->getConnectionName()); - } - - /** - * @return AmqpConsumer|MockObject|AmqpConsumer - */ - private function createConsumerMock() - { - return $this->createMock(AmqpConsumer::class); - } - - /** - * @return MockObject|RabbitMQQueue|RabbitMQQueue - */ - private function createRabbitMQQueueMock() - { - return $this->createMock(RabbitMQQueue::class); - } -} diff --git a/tests/Unit/Queue/RabbitMQQueueTest.php b/tests/Unit/Queue/RabbitMQQueueTest.php deleted file mode 100644 index 7128cce4..00000000 --- a/tests/Unit/Queue/RabbitMQQueueTest.php +++ /dev/null @@ -1,377 +0,0 @@ -assertTrue($rc->implementsInterface(QueueContract::class)); - } - - public function testShouldBeSubClassOfQueue(): void - { - $rc = new ReflectionClass(RabbitMQQueue::class); - - $this->assertTrue($rc->isSubclassOf(Queue::class)); - } - - public function testCouldBeConstructedWithExpectedArguments(): void - { - new RabbitMQQueue($this->createAmqpContext(), $this->createDummyConfig()); - } - - public function testShouldGenerateNewCorrelationIdIfNotSet(): void - { - $queue = new RabbitMQQueue($this->createAmqpContext(), $this->createDummyConfig()); - - $firstId = $queue->getCorrelationId(); - $secondId = $queue->getCorrelationId(); - - $this->assertNotEmpty($firstId); - $this->assertNotEmpty($secondId); - $this->assertNotSame($firstId, $secondId); - } - - public function testShouldReturnPreviouslySetCorrelationId(): void - { - $expectedId = 'theCorrelationId'; - - $queue = new RabbitMQQueue($this->createAmqpContext(), $this->createDummyConfig()); - - $queue->setCorrelationId($expectedId); - - $this->assertSame($expectedId, $queue->getCorrelationId()); - $this->assertSame($expectedId, $queue->getCorrelationId()); - } - - public function testShouldAllowGetContextSetInConstructor(): void - { - $context = $this->createAmqpContext(); - - $queue = new RabbitMQQueue($context, $this->createDummyConfig()); - - $this->assertSame($context, $queue->getContext()); - } - - public function testShouldReturnExpectedNumberOfMessages(): void - { - $expectedQueueName = 'theQueueName'; - $queue = $this->createMock(AmqpQueue::class); - $expectedCount = 123321; - - $context = $this->createAmqpContext(); - $context - ->expects($this->once()) - ->method('createTopic') - ->willReturn($this->createMock(AmqpTopic::class)); - $context - ->expects($this->once()) - ->method('createQueue') - ->with($expectedQueueName) - ->willReturn($queue); - $context - ->expects($this->once()) - ->method('declareQueue') - ->with($this->identicalTo($queue)) - ->willReturn($expectedCount); - - $queue = new RabbitMQQueue($context, $this->createDummyConfig()); - $queue->setContainer($this->createDummyContainer()); - - $this->assertSame($expectedCount, $queue->size($expectedQueueName)); - } - - /** - * @throws Exception - */ - public function testShouldSendExpectedMessageOnPushRaw(): void - { - $expectedQueueName = 'theQueueName'; - $expectedBody = 'thePayload'; - $topic = $this->createMock(AmqpTopic::class); - - $queue = $this->createMock(AmqpQueue::class); - $queue->method('getQueueName')->willReturn('theQueueName'); - - $producer = $this->createMock(AmqpProducer::class); - $producer - ->expects($this->once()) - ->method('send') - ->with($this->identicalTo($topic), $this->isInstanceOf(AmqpMessage::class)) - ->willReturnCallback(function ($actualTopic, AmqpMessage $message) use ($expectedQueueName, $expectedBody, $topic): void { - $this->assertSame($topic, $actualTopic); - $this->assertSame($expectedBody, $message->getBody()); - $this->assertSame($expectedQueueName, $message->getRoutingKey()); - $this->assertSame('application/json', $message->getContentType()); - $this->assertSame(AmqpMessage::DELIVERY_MODE_PERSISTENT, $message->getDeliveryMode()); - $this->assertNotEmpty($message->getCorrelationId()); - $this->assertNull($message->getProperty(RabbitMQJob::ATTEMPT_COUNT_HEADERS_KEY)); - }); - $producer - ->expects($this->never()) - ->method('setDeliveryDelay'); - - $context = $this->createAmqpContext(); - $context - ->expects($this->once()) - ->method('createTopic') - ->willReturn($topic); - $context - ->expects($this->once()) - ->method('createMessage') - ->with($expectedBody) - ->willReturn(new \Interop\Amqp\Impl\AmqpMessage($expectedBody)); - - $context - ->expects($this->once()) - ->method('createQueue') - ->with($expectedQueueName) - ->willReturn($queue); - $context - ->expects($this->once()) - ->method('createProducer') - ->willReturn($producer); - - $queue = new RabbitMQQueue($context, $this->createDummyConfig()); - $queue->setContainer($this->createDummyContainer()); - - $queue->pushRaw('thePayload', $expectedQueueName); - } - - /** - * @throws Exception - */ - public function testShouldSetAttemptCountPropIfNotNull(): void - { - $expectedAttempts = 54321; - - $topic = $this->createMock(AmqpTopic::class); - - $producer = $this->createMock(AmqpProducer::class); - $producer - ->expects($this->once()) - ->method('send') - ->with($this->identicalTo($topic), $this->isInstanceOf(AmqpMessage::class)) - ->willReturnCallback(function ($actualTopic, AmqpMessage $message) use ($expectedAttempts): void { - $this->assertSame($expectedAttempts, $message->getProperty(RabbitMQJob::ATTEMPT_COUNT_HEADERS_KEY)); - }); - $producer - ->expects($this->never()) - ->method('setDeliveryDelay'); - - $context = $this->createAmqpContext(); - $context - ->expects($this->once()) - ->method('createTopic') - ->willReturn($topic); - $context - ->expects($this->once()) - ->method('createMessage') - ->with() - ->willReturn(new \Interop\Amqp\Impl\AmqpMessage()); - $context - ->expects($this->once()) - ->method('createQueue') - ->willReturn($this->createMock(AmqpQueue::class)); - $context - ->expects($this->once()) - ->method('createProducer') - ->willReturn($producer); - - $queue = new RabbitMQQueue($context, $this->createDummyConfig()); - $queue->setContainer($this->createDummyContainer()); - - $queue->pushRaw('thePayload', 'aQueue', ['attempts' => $expectedAttempts]); - } - - /** - * @throws Exception - */ - public function testShouldSetDeliveryDelayIfDelayOptionPresent(): void - { - $expectedDelay = 56; - $expectedDeliveryDelay = 56000; - - $topic = $this->createMock(AmqpTopic::class); - - $producer = $this->createMock(AmqpProducer::class); - $producer - ->expects($this->once()) - ->method('send'); - $producer - ->expects($this->once()) - ->method('setDeliveryDelay') - ->with($expectedDeliveryDelay); - - $context = $this->createAmqpContext(); - $context - ->expects($this->once()) - ->method('createTopic') - ->willReturn($topic); - $context - ->expects($this->once()) - ->method('createMessage') - ->with() - ->willReturn(new \Interop\Amqp\Impl\AmqpMessage()); - $context - ->expects($this->once()) - ->method('createQueue') - ->willReturn($this->createMock(AmqpQueue::class)); - $context - ->expects($this->once()) - ->method('createProducer') - ->willReturn($producer); - - $queue = new RabbitMQQueue($context, $this->createDummyConfig()); - $queue->setContainer($this->createDummyContainer()); - - $queue->pushRaw('thePayload', 'aQueue', ['delay' => $expectedDelay]); - } - - public function testShouldReturnNullIfNoMessagesOnQueue(): void - { - $queue = $this->createMock(AmqpQueue::class); - - $consumer = $this->createMock(AmqpConsumer::class); - $consumer - ->expects($this->once()) - ->method('receiveNoWait') - ->willReturn(null); - - $context = $this->createAmqpContext(); - $context - ->expects($this->once()) - ->method('createTopic') - ->willReturn($this->createMock(AmqpTopic::class)); - $context - ->expects($this->once()) - ->method('createQueue') - ->willReturn($queue); - $context - ->expects($this->once()) - ->method('createConsumer') - ->with($this->identicalTo($queue)) - ->willReturn($consumer); - - $queue = new RabbitMQQueue($context, $this->createDummyConfig()); - $queue->setContainer($this->createDummyContainer()); - - $this->assertNull($queue->pop('aQueue')); - } - - public function testShouldReturnRabbitMQJobIfMessageReceivedFromQueue(): void - { - $queue = $this->createMock(AmqpQueue::class); - - $message = new \Interop\Amqp\Impl\AmqpMessage('thePayload'); - - $consumer = $this->createMock(AmqpConsumer::class); - $consumer - ->expects($this->once()) - ->method('receiveNoWait') - ->willReturn($message); - $consumer - ->expects($this->once()) - ->method('getQueue') - ->willReturn($queue); - - $context = $this->createAmqpContext(); - $context - ->expects($this->once()) - ->method('createTopic') - ->willReturn($this->createMock(AmqpTopic::class)); - $context - ->expects($this->once()) - ->method('createQueue') - ->willReturn($queue); - $context - ->expects($this->once()) - ->method('createConsumer') - ->with($this->identicalTo($queue)) - ->willReturn($consumer); - - $queue = new RabbitMQQueue($context, $this->createDummyConfig()); - $queue->setContainer($this->createDummyContainer()); - - $job = $queue->pop('aQueue'); - - $this->assertInstanceOf(RabbitMQJob::class, $job); - } - - /** - * @return AmqpContext|MockObject|AmqpContext - */ - private function createAmqpContext() - { - return $this->createMock(AmqpContext::class); - } - - private function createDummyContainer(): Container - { - return new Container(); - } - - /** - * @return array - */ - private function createDummyConfig(): array - { - return [ - 'dsn' => 'aDsn', - 'host' => 'aHost', - 'port' => 'aPort', - 'login' => 'aLogin', - 'password' => 'aPassword', - 'vhost' => 'aVhost', - 'ssl_params' => [ - 'ssl_on' => 'aSslOn', - 'verify_peer' => 'aVerifyPeer', - 'cafile' => 'aCafile', - 'local_cert' => 'aLocalCert', - 'local_key' => 'aLocalKey', - ], - 'options' => [ - 'exchange' => [ - 'name' => 'anExchangeName', - 'declare' => false, - 'type' => AmqpTopic::TYPE_DIRECT, - 'passive' => false, - 'durable' => true, - 'auto_delete' => false, - ], - - 'queue' => [ - 'name' => 'aQueueName', - 'declare' => false, - 'bind' => false, - 'passive' => false, - 'durable' => true, - 'exclusive' => false, - 'auto_delete' => false, - 'arguments' => '[]', - ], - ], - 'sleep_on_error' => false, - ]; - } -} From bcb92ccf2c51a3882e4792688397bc7021f76e03 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Mon, 25 Nov 2019 16:32:20 +0300 Subject: [PATCH 090/375] wip --- src/Queue/Connectors/RabbitMQConnector.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Queue/Connectors/RabbitMQConnector.php b/src/Queue/Connectors/RabbitMQConnector.php index bebe27e4..7ef98310 100644 --- a/src/Queue/Connectors/RabbitMQConnector.php +++ b/src/Queue/Connectors/RabbitMQConnector.php @@ -59,7 +59,7 @@ public function connect(array $config): Queue } if ($worker instanceof RabbitMQQueue) { - return new $worker($connection, $config); + return new $worker($connection, $channel, $config); } throw new InvalidArgumentException('Invalid worker.'); From 08217cc874190a0e05e7aa4f2735e2104e34719c Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Mon, 25 Nov 2019 16:34:55 +0300 Subject: [PATCH 091/375] wip --- src/Console/ConsumeCommand.php | 7 +++---- src/Console/ExchangeMakeCommand.php | 4 ++-- src/Console/QueueBindCommand.php | 2 +- src/Console/QueueMakeCommand.php | 2 +- src/Queue/RabbitMQQueue.php | 3 +-- 5 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/Console/ConsumeCommand.php b/src/Console/ConsumeCommand.php index aec35f9f..3c8cfa74 100644 --- a/src/Console/ConsumeCommand.php +++ b/src/Console/ConsumeCommand.php @@ -19,7 +19,7 @@ class ConsumeCommand extends Command public function handle(RabbitMQConnector $connector): void { - $config = $this->laravel['config']->get('queue.connections.' . $this->argument('connection')); + $config = $this->laravel['config']->get('queue.connections.'.$this->argument('connection')); $queue = $connector->connect($config); @@ -35,14 +35,13 @@ public function handle(RabbitMQConnector $connector): void [$this, 'processMessage'] ); - while($channel->is_consuming()) { + while ($channel->is_consuming()) { $channel->wait(); } } protected function processMessage(AMQPMessage $AMQPMessage): void { - } protected function consumerTag(): string @@ -51,6 +50,6 @@ protected function consumerTag(): string return (string)$this->option('consumer-tag'); } - return config('app.name') . '_' . getmygid(); + return config('app.name').'_'.getmygid(); } } diff --git a/src/Console/ExchangeMakeCommand.php b/src/Console/ExchangeMakeCommand.php index 68cd5ad3..67f4b41a 100644 --- a/src/Console/ExchangeMakeCommand.php +++ b/src/Console/ExchangeMakeCommand.php @@ -25,7 +25,7 @@ class ExchangeMakeCommand extends Command */ public function handle(RabbitMQConnector $connector): void { - $config = $this->laravel['config']->get('queue.connections.' . $this->argument('connection')); + $config = $this->laravel['config']->get('queue.connections.'.$this->argument('connection')); $queue = $connector->connect($config); $channel = $queue->getChannel(); @@ -33,7 +33,7 @@ public function handle(RabbitMQConnector $connector): void try { $channel->exchange_declare($this->argument('name'), '', true); } catch (AMQPProtocolChannelException $exception) { - if($exception->amqp_reply_code === 404) { + if ($exception->amqp_reply_code === 404) { $this->declareExchange($queue->getConnection()->channel()); $this->info('Exchange declared successfully.'); diff --git a/src/Console/QueueBindCommand.php b/src/Console/QueueBindCommand.php index 56eaf507..2dfed64d 100644 --- a/src/Console/QueueBindCommand.php +++ b/src/Console/QueueBindCommand.php @@ -21,7 +21,7 @@ class QueueBindCommand extends Command */ public function handle(RabbitMQConnector $connector): void { - $config = $this->laravel['config']->get('queue.connections.' . $this->argument('connection')); + $config = $this->laravel['config']->get('queue.connections.'.$this->argument('connection')); $queue = $connector->connect($config); diff --git a/src/Console/QueueMakeCommand.php b/src/Console/QueueMakeCommand.php index 942fb726..9bde4416 100644 --- a/src/Console/QueueMakeCommand.php +++ b/src/Console/QueueMakeCommand.php @@ -24,7 +24,7 @@ class QueueMakeCommand extends Command */ public function handle(RabbitMQConnector $connector): void { - $config = $this->laravel['config']->get('queue.connections.' . $this->argument('connection')); + $config = $this->laravel['config']->get('queue.connections.'.$this->argument('connection')); $queue = $connector->connect($config); diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index 47cae98f..c82acfc2 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -48,8 +48,7 @@ public function __construct( AbstractConnection $connection, AMQPChannel $channel, string $default - ) - { + ) { $this->connection = $connection; $this->channel = $channel; $this->default = $default; From 0ceac457aad0e43aaf38de1dfb94d4c38558ef59 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Mon, 25 Nov 2019 17:35:21 +0300 Subject: [PATCH 092/375] wip --- config/rabbitmq.php | 10 +-- docker-compose.yml | 20 ++++- phpunit.xml.dist | 2 +- src/Console/ConsumeCommand.php | 14 +++- src/Queue/Connectors/RabbitMQConnector.php | 20 ++++- tests/Feature/ConnectorTest.php | 94 ++++++++++++++++++++++ tests/Feature/SslQueueTest.php | 3 +- 7 files changed, 149 insertions(+), 14 deletions(-) create mode 100644 tests/Feature/ConnectorTest.php diff --git a/config/rabbitmq.php b/config/rabbitmq.php index cd2c008f..765d73b8 100644 --- a/config/rabbitmq.php +++ b/config/rabbitmq.php @@ -23,11 +23,11 @@ 'options' => [ 'ssl_options' => [ - 'cafile' => env('RABBITMQ_OPTIONS_SSL_CAFILE', null), - 'local_cert' => env('RABBITMQ_OPTIONS_SSL_LOCALCERT', null), - 'local_key' => env('RABBITMQ_OPTIONS_SSL_LOCALKEY', null), - 'verify_peer' => env('RABBITMQ_OPTIONS_SSL_VERIFY_PEER', true), - 'passphrase' => env('RABBITMQ_OPTIONS_SSL_PASSPHRASE', null), + 'cafile' => env('RABBITMQ_SSL_CAFILE', null), + 'local_cert' => env('RABBITMQ_SSL_LOCALCERT', null), + 'local_key' => env('RABBITMQ_SSL_LOCALKEY', null), + 'verify_peer' => env('RABBITMQ_SSL_VERIFY_PEER', true), + 'passphrase' => env('RABBITMQ_SSL_PASSPHRASE', null), ], ], diff --git a/docker-compose.yml b/docker-compose.yml index 4791bb01..d019af41 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,7 +2,7 @@ version: '3.7' services: - rabbitmq: + rabbitmq-management: image: rabbitmq:management environment: RABBITMQ_DEFAULT_USER: guest @@ -21,3 +21,21 @@ services: - 15672:15672 - 5671:5671 - 5672:5672 + + rabbitmq: + image: rabbitmq + environment: + RABBITMQ_DEFAULT_USER: guest + RABBITMQ_DEFAULT_PASSWORD: guest + RABBITMQ_DEFAULT_VHOST: / + RABBITMQ_SSL_CACERTFILE: /rootCA.pem + RABBITMQ_SSL_CERTFILE: /rootCA.pem + RABBITMQ_SSL_KEYFILE: /rootCA.key + RABBITMQ_SSL_VERIFY: verify_none + RABBITMQ_SSL_FAIL_IF_NO_PEER_CERT: "false" + volumes: + - "./tests/files/rootCA.pem:/rootCA.pem:ro" + - "./tests/files/rootCA.key:/rootCA.key:ro" + ports: + - 5671:5671 + - 5672:5672 diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 4ebd0b75..d31909dc 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -21,7 +21,7 @@ - + diff --git a/src/Console/ConsumeCommand.php b/src/Console/ConsumeCommand.php index 3c8cfa74..a66832e5 100644 --- a/src/Console/ConsumeCommand.php +++ b/src/Console/ConsumeCommand.php @@ -2,6 +2,7 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Console; +use ErrorException; use Illuminate\Console\Command; use PhpAmqpLib\Message\AMQPMessage; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Connectors\RabbitMQConnector; @@ -17,6 +18,10 @@ class ConsumeCommand extends Command {--exclusive}'; protected $description = ''; + /** + * @param RabbitMQConnector $connector + * @throws ErrorException + */ public function handle(RabbitMQConnector $connector): void { $config = $this->laravel['config']->get('queue.connections.'.$this->argument('connection')); @@ -40,10 +45,6 @@ public function handle(RabbitMQConnector $connector): void } } - protected function processMessage(AMQPMessage $AMQPMessage): void - { - } - protected function consumerTag(): string { if ($this->option('consumer-tag')) { @@ -52,4 +53,9 @@ protected function consumerTag(): string return config('app.name').'_'.getmygid(); } + + protected function processMessage(AMQPMessage $AMQPMessage): void + { + // + } } diff --git a/src/Queue/Connectors/RabbitMQConnector.php b/src/Queue/Connectors/RabbitMQConnector.php index 7ef98310..02b201ad 100644 --- a/src/Queue/Connectors/RabbitMQConnector.php +++ b/src/Queue/Connectors/RabbitMQConnector.php @@ -77,7 +77,25 @@ protected function createConnection(array $config): AbstractConnection return $connection::create_connection( Arr::get($config, 'hosts', []), - Arr::get($config, 'options', []) + $this->filter(Arr::get($config, 'options', [])) ); } + + private function filter(array $array): array + { + foreach ($array as $index => &$value) { + if (is_array($value)) { + $value = $this->filter($value); + continue; + } + + // If the value is null then remove it. + if ($value === null) { + unset($array[$index]); + continue; + } + } + + return $array; + } } diff --git a/tests/Feature/ConnectorTest.php b/tests/Feature/ConnectorTest.php new file mode 100644 index 00000000..2f170364 --- /dev/null +++ b/tests/Feature/ConnectorTest.php @@ -0,0 +1,94 @@ +app['config']->set('queue.connections.rabbitmq', [ + 'driver' => 'rabbitmq', + 'queue' => env('RABBITMQ_QUEUE', 'default'), + 'connection' => AMQPLazyConnection::class, + + 'hosts' => [ + [ + 'host' => getenv('HOST'), + 'port' => getenv('PORT'), + 'user' => 'guest', + 'password' => 'guest', + 'vhost' => '/', + ], + ], + + 'options' => [ + 'ssl_options' => [ + 'cafile' => env('RABBITMQ_SSL_CAFILE', null), + 'local_cert' => env('RABBITMQ_SSL_LOCALCERT', null), + 'local_key' => env('RABBITMQ_SSL_LOCALKEY', null), + 'verify_peer' => env('RABBITMQ_SSL_VERIFY_PEER', true), + 'passphrase' => env('RABBITMQ_SSL_PASSPHRASE', null), + ], + ], + + 'worker' => env('RABBITMQ_WORKER', 'default'), + ]); + + /** @var QueueManager $queue */ + $queue = $this->app['queue']; + + /** @var RabbitMQQueue $connection */ + $connection = $queue->connection('rabbitmq'); + + $this->assertInstanceOf(RabbitMQQueue::class, $connection); + $this->assertInstanceOf(AMQPLazyConnection::class, $connection->getConnection()); + $this->assertTrue($connection->getConnection()->isConnected()); + $this->assertTrue($connection->getChannel()->is_open()); + } + + public function testSslConnection(): void + { + $this->app['config']->set('queue.connections.rabbitmq', [ + 'driver' => 'rabbitmq', + 'queue' => env('RABBITMQ_QUEUE', 'default'), + 'connection' => AMQPSSLConnection::class, + + 'hosts' => [ + [ + 'host' => getenv('HOST'), + 'port' => getenv('PORT_SSL'), + 'user' => 'guest', + 'password' => 'guest', + 'vhost' => '/', + ], + ], + + 'options' => [ + 'ssl_options' => [ + 'cafile' => env('RABBITMQ_SSL_CAFILE', null), + 'local_cert' => env('RABBITMQ_SSL_LOCALCERT', null), + 'local_key' => env('RABBITMQ_SSL_LOCALKEY', null), + 'verify_peer' => env('RABBITMQ_SSL_VERIFY_PEER', true), + 'passphrase' => env('RABBITMQ_SSL_PASSPHRASE', null), + ], + ], + + 'worker' => env('RABBITMQ_WORKER', 'default'), + ]); + + /** @var QueueManager $queue */ + $queue = $this->app['queue']; + + /** @var RabbitMQQueue $connection */ + $connection = $queue->connection('rabbitmq'); + $this->assertInstanceOf(RabbitMQQueue::class, $connection); + $this->assertInstanceOf(AMQPSSLConnection::class, $connection->getConnection()); + $this->assertTrue($connection->getConnection()->isConnected()); + $this->assertTrue($connection->getChannel()->is_open()); + } +} diff --git a/tests/Feature/SslQueueTest.php b/tests/Feature/SslQueueTest.php index 7cc01f5a..5632f86e 100644 --- a/tests/Feature/SslQueueTest.php +++ b/tests/Feature/SslQueueTest.php @@ -2,7 +2,6 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Feature; -use PhpAmqpLib\Connection\AMQPLazyConnection; use PhpAmqpLib\Connection\AMQPSSLConnection; /** @@ -30,7 +29,7 @@ protected function getEnvironmentSetUp($app): void 'options' => [ 'ssl_options' => [ - 'cafile' => getenv('RABBITMQ_OPTIONS_SSL_CAFILE'), + 'cafile' => getenv('RABBITMQ_SSL_CAFILE'), 'local_cert' => null, 'local_key' => null, 'verify_peer' => true, From bdf982c1674ba8327897ced319a2a4d04a9ddef1 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Mon, 25 Nov 2019 17:46:43 +0300 Subject: [PATCH 093/375] wip --- .travis.yml | 2 +- README.md | 2 +- docker-compose.yml | 30 ++++++++++++++++-------------- tests/Feature/ConnectorTest.php | 10 +++++----- tests/Feature/SslQueueTest.php | 1 - 5 files changed, 23 insertions(+), 22 deletions(-) diff --git a/.travis.yml b/.travis.yml index d64690fa..fa1c2ebe 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,7 +19,7 @@ before_install: - sudo mv docker-compose /usr/local/bin before_script: - - docker-compose up -d + - docker-compose up -d rabbitmq - travis_retry composer self-update - travis_retry composer update --no-progress --no-interaction --prefer-dist - sleep 10 diff --git a/README.md b/README.md index d9b8925f..8e4d0d0f 100644 --- a/README.md +++ b/README.md @@ -157,7 +157,7 @@ $app->register(VladimirYuldashev\LaravelQueueRabbitMQ\LaravelQueueRabbitMQServic Setup RabbitMQ using `docker-compose`: ```bash -docker-compose up -d +docker-compose up -d rabbitmq ``` To run the test suite you can use the following commands: diff --git a/docker-compose.yml b/docker-compose.yml index d019af41..6052f7f9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,17 +2,17 @@ version: '3.7' services: - rabbitmq-management: - image: rabbitmq:management + rabbitmq: + image: rabbitmq environment: RABBITMQ_DEFAULT_USER: guest RABBITMQ_DEFAULT_PASSWORD: guest RABBITMQ_DEFAULT_VHOST: / - RABBITMQ_MANAGEMENT_SSL_CACERTFILE: /rootCA.pem - RABBITMQ_MANAGEMENT_SSL_CERTFILE: /rootCA.pem - RABBITMQ_MANAGEMENT_SSL_KEYFILE: /rootCA.key - RABBITMQ_MANAGEMENT_SSL_VERIFY: verify_none - RABBITMQ_MANAGEMENT_SSL_FAIL_IF_NO_PEER_CERT: "false" + RABBITMQ_SSL_CACERTFILE: /rootCA.pem + RABBITMQ_SSL_CERTFILE: /rootCA.pem + RABBITMQ_SSL_KEYFILE: /rootCA.key + RABBITMQ_SSL_VERIFY: verify_none + RABBITMQ_SSL_FAIL_IF_NO_PEER_CERT: "false" volumes: - "./tests/files/rootCA.pem:/rootCA.pem:ro" - "./tests/files/rootCA.key:/rootCA.key:ro" @@ -22,20 +22,22 @@ services: - 5671:5671 - 5672:5672 - rabbitmq: - image: rabbitmq + rabbitmq-management: + image: rabbitmq:management environment: RABBITMQ_DEFAULT_USER: guest RABBITMQ_DEFAULT_PASSWORD: guest RABBITMQ_DEFAULT_VHOST: / - RABBITMQ_SSL_CACERTFILE: /rootCA.pem - RABBITMQ_SSL_CERTFILE: /rootCA.pem - RABBITMQ_SSL_KEYFILE: /rootCA.key - RABBITMQ_SSL_VERIFY: verify_none - RABBITMQ_SSL_FAIL_IF_NO_PEER_CERT: "false" + RABBITMQ_MANAGEMENT_SSL_CACERTFILE: /rootCA.pem + RABBITMQ_MANAGEMENT_SSL_CERTFILE: /rootCA.pem + RABBITMQ_MANAGEMENT_SSL_KEYFILE: /rootCA.key + RABBITMQ_MANAGEMENT_SSL_VERIFY: verify_none + RABBITMQ_MANAGEMENT_SSL_FAIL_IF_NO_PEER_CERT: "false" volumes: - "./tests/files/rootCA.pem:/rootCA.pem:ro" - "./tests/files/rootCA.key:/rootCA.key:ro" ports: + - 15671:15671 + - 15672:15672 - 5671:5671 - 5672:5672 diff --git a/tests/Feature/ConnectorTest.php b/tests/Feature/ConnectorTest.php index 2f170364..2c58874a 100644 --- a/tests/Feature/ConnectorTest.php +++ b/tests/Feature/ConnectorTest.php @@ -70,11 +70,11 @@ public function testSslConnection(): void 'options' => [ 'ssl_options' => [ - 'cafile' => env('RABBITMQ_SSL_CAFILE', null), - 'local_cert' => env('RABBITMQ_SSL_LOCALCERT', null), - 'local_key' => env('RABBITMQ_SSL_LOCALKEY', null), - 'verify_peer' => env('RABBITMQ_SSL_VERIFY_PEER', true), - 'passphrase' => env('RABBITMQ_SSL_PASSPHRASE', null), + 'cafile' => getenv('RABBITMQ_SSL_CAFILE'), + 'local_cert' => null, + 'local_key' => null, + 'verify_peer' => true, + 'passphrase' => null, ], ], diff --git a/tests/Feature/SslQueueTest.php b/tests/Feature/SslQueueTest.php index 5632f86e..54969a9c 100644 --- a/tests/Feature/SslQueueTest.php +++ b/tests/Feature/SslQueueTest.php @@ -38,7 +38,6 @@ protected function getEnvironmentSetUp($app): void ], 'worker' => 'default', - ]); } From cfa3974fe79dad31cf8c56160a5effa174224a9d Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Mon, 25 Nov 2019 17:52:20 +0300 Subject: [PATCH 094/375] wip --- .travis.yml | 1 - src/Console/ConsumeCommand.php | 61 --------------------- src/LaravelQueueRabbitMQServiceProvider.php | 1 - 3 files changed, 63 deletions(-) delete mode 100644 src/Console/ConsumeCommand.php diff --git a/.travis.yml b/.travis.yml index fa1c2ebe..960328da 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,6 @@ language: php php: - 7.2 - 7.3 - - 7.4snapshot services: - docker diff --git a/src/Console/ConsumeCommand.php b/src/Console/ConsumeCommand.php deleted file mode 100644 index a66832e5..00000000 --- a/src/Console/ConsumeCommand.php +++ /dev/null @@ -1,61 +0,0 @@ -laravel['config']->get('queue.connections.'.$this->argument('connection')); - - $queue = $connector->connect($config); - - $channel = $queue->getChannel(); - - $channel->basic_consume( - $this->argument('queue'), - $this->consumerTag(), - $this->option('no-local'), - $this->option('no-ack'), - $this->option('exclusive'), - false, - [$this, 'processMessage'] - ); - - while ($channel->is_consuming()) { - $channel->wait(); - } - } - - protected function consumerTag(): string - { - if ($this->option('consumer-tag')) { - return (string)$this->option('consumer-tag'); - } - - return config('app.name').'_'.getmygid(); - } - - protected function processMessage(AMQPMessage $AMQPMessage): void - { - // - } -} diff --git a/src/LaravelQueueRabbitMQServiceProvider.php b/src/LaravelQueueRabbitMQServiceProvider.php index 1093bed2..f1e8ecad 100644 --- a/src/LaravelQueueRabbitMQServiceProvider.php +++ b/src/LaravelQueueRabbitMQServiceProvider.php @@ -23,7 +23,6 @@ public function register(): void if ($this->app->runningInConsole()) { $this->commands([ - Console\ConsumeCommand::class, Console\ExchangeMakeCommand::class, Console\QueueBindCommand::class, Console\QueueMakeCommand::class, From e4c659e13615c6f5d396f83c00fc67c54d41084d Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Mon, 25 Nov 2019 18:06:26 +0300 Subject: [PATCH 095/375] wip --- ...Command.php => ExchangeDeclareCommand.php} | 8 +++---- src/Console/QueueBindCommand.php | 7 ++++-- ...akeCommand.php => QueueDeclareCommand.php} | 6 ++--- src/Console/QueuePurgeCommand.php | 24 +++++++++++++++++-- src/LaravelQueueRabbitMQServiceProvider.php | 4 ++-- 5 files changed, 36 insertions(+), 13 deletions(-) rename src/Console/{ExchangeMakeCommand.php => ExchangeDeclareCommand.php} (90%) rename src/Console/{QueueMakeCommand.php => QueueDeclareCommand.php} (93%) diff --git a/src/Console/ExchangeMakeCommand.php b/src/Console/ExchangeDeclareCommand.php similarity index 90% rename from src/Console/ExchangeMakeCommand.php rename to src/Console/ExchangeDeclareCommand.php index 67f4b41a..ed4a149a 100644 --- a/src/Console/ExchangeMakeCommand.php +++ b/src/Console/ExchangeDeclareCommand.php @@ -8,16 +8,16 @@ use PhpAmqpLib\Exception\AMQPProtocolChannelException; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Connectors\RabbitMQConnector; -class ExchangeMakeCommand extends Command +class ExchangeDeclareCommand extends Command { - protected $signature = 'rabbitmq:make-exchange + protected $signature = 'rabbitmq:exchange-declare {name : The name of the exchange to declare} - {connection=rabbitmq : The name of the queue connection to work} + {connection=rabbitmq : The name of the queue connection to use} {--type=direct} {--durable} {--auto-delete}'; - protected $description = ''; + protected $description = 'Declare exchange'; /** * @param RabbitMQConnector $connector diff --git a/src/Console/QueueBindCommand.php b/src/Console/QueueBindCommand.php index 2dfed64d..8b3e8653 100644 --- a/src/Console/QueueBindCommand.php +++ b/src/Console/QueueBindCommand.php @@ -11,9 +11,10 @@ class QueueBindCommand extends Command protected $signature = 'rabbitmq:queue-bind {queue} {exchange} - {connection=rabbitmq : The name of the queue connection to work} + {connection=rabbitmq : The name of the queue connection to use} {--routing-key}'; - protected $description = ''; + + protected $description = 'Bind queue to exchange'; /** * @param RabbitMQConnector $connector @@ -32,5 +33,7 @@ public function handle(RabbitMQConnector $connector): void $this->argument('exchange'), (string) $this->option('routing-key') ); + + $this->info('Queue bound to exchange successfully.'); } } diff --git a/src/Console/QueueMakeCommand.php b/src/Console/QueueDeclareCommand.php similarity index 93% rename from src/Console/QueueMakeCommand.php rename to src/Console/QueueDeclareCommand.php index 9bde4416..6dc1cae6 100644 --- a/src/Console/QueueMakeCommand.php +++ b/src/Console/QueueDeclareCommand.php @@ -8,11 +8,11 @@ use PhpAmqpLib\Exception\AMQPProtocolChannelException; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Connectors\RabbitMQConnector; -class QueueMakeCommand extends Command +class QueueDeclareCommand extends Command { - protected $signature = 'rabbitmq:make-queue + protected $signature = 'rabbitmq:queue-declare {name : The name of the queue to declare} - {connection=rabbitmq : The name of the queue connection to work} + {connection=rabbitmq : The name of the queue connection to use} {--durable} {--auto-delete}'; diff --git a/src/Console/QueuePurgeCommand.php b/src/Console/QueuePurgeCommand.php index 4ce09253..b4a418da 100644 --- a/src/Console/QueuePurgeCommand.php +++ b/src/Console/QueuePurgeCommand.php @@ -2,10 +2,30 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Console; +use Exception; use Illuminate\Console\Command; +use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Connectors\RabbitMQConnector; class QueuePurgeCommand extends Command { - protected $signature = 'rabbitmq:queue-purge'; - protected $description = ''; + protected $signature = 'rabbitmq:queue-purge + {queue} + {connection=rabbitmq : The name of the queue connection to use}'; + + protected $description = 'Purge queue'; + + /** + * @param RabbitMQConnector $connector + * @throws Exception + */ + public function handle(RabbitMQConnector $connector): void + { + $config = $this->laravel['config']->get('queue.connections.'.$this->argument('connection')); + + $queue = $connector->connect($config); + + $queue->purge($this->argument('queue')); + + $this->info('Queue purged successfully.'); + } } diff --git a/src/LaravelQueueRabbitMQServiceProvider.php b/src/LaravelQueueRabbitMQServiceProvider.php index f1e8ecad..4e24a98e 100644 --- a/src/LaravelQueueRabbitMQServiceProvider.php +++ b/src/LaravelQueueRabbitMQServiceProvider.php @@ -23,9 +23,9 @@ public function register(): void if ($this->app->runningInConsole()) { $this->commands([ - Console\ExchangeMakeCommand::class, + Console\ExchangeDeclareCommand::class, Console\QueueBindCommand::class, - Console\QueueMakeCommand::class, + Console\QueueDeclareCommand::class, Console\QueuePurgeCommand::class, ]); } From 8b7356cb839950dc1157033946c78f31e1f696c6 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Mon, 25 Nov 2019 18:24:57 +0300 Subject: [PATCH 096/375] wip --- src/Console/ExchangeDeclareCommand.php | 39 ++++---------- src/Console/QueueBindCommand.php | 4 +- src/Console/QueueDeclareCommand.php | 36 +++---------- src/Queue/RabbitMQQueue.php | 72 ++++++++++++++++++++++++++ 4 files changed, 92 insertions(+), 59 deletions(-) diff --git a/src/Console/ExchangeDeclareCommand.php b/src/Console/ExchangeDeclareCommand.php index ed4a149a..bc9c2f71 100644 --- a/src/Console/ExchangeDeclareCommand.php +++ b/src/Console/ExchangeDeclareCommand.php @@ -4,8 +4,6 @@ use Exception; use Illuminate\Console\Command; -use PhpAmqpLib\Channel\AMQPChannel; -use PhpAmqpLib\Exception\AMQPProtocolChannelException; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Connectors\RabbitMQConnector; class ExchangeDeclareCommand extends Command @@ -14,8 +12,8 @@ class ExchangeDeclareCommand extends Command {name : The name of the exchange to declare} {connection=rabbitmq : The name of the queue connection to use} {--type=direct} - {--durable} - {--auto-delete}'; + {--durable=1} + {--auto-delete=0}'; protected $description = 'Declare exchange'; @@ -28,35 +26,20 @@ public function handle(RabbitMQConnector $connector): void $config = $this->laravel['config']->get('queue.connections.'.$this->argument('connection')); $queue = $connector->connect($config); - $channel = $queue->getChannel(); - try { - $channel->exchange_declare($this->argument('name'), '', true); - } catch (AMQPProtocolChannelException $exception) { - if ($exception->amqp_reply_code === 404) { - $this->declareExchange($queue->getConnection()->channel()); + if ($queue->isExchangeExists($this->argument('name'))) { + $this->warn('Exchange already exists.'); - $this->info('Exchange declared successfully.'); - - return; - } - - throw $exception; + return; } - $this->warn('Exchange already exists.'); - } - - protected function declareExchange(AMQPChannel $channel): void - { - $channel->exchange_declare( + $queue->declareExchange( $this->argument('name'), - $this->option('type'), - false, - $this->option('durable'), - $this->option('auto-delete'), - false, - true + $this->argument('type'), + (bool)$this->option('durable'), + (bool)$this->option('auto-delete') ); + + $this->warn('Exchange already exists.'); } } diff --git a/src/Console/QueueBindCommand.php b/src/Console/QueueBindCommand.php index 8b3e8653..47c4ea3a 100644 --- a/src/Console/QueueBindCommand.php +++ b/src/Console/QueueBindCommand.php @@ -26,9 +26,7 @@ public function handle(RabbitMQConnector $connector): void $queue = $connector->connect($config); - $channel = $queue->getChannel(); - - $channel->queue_bind( + $queue->bindQueue( $this->argument('queue'), $this->argument('exchange'), (string) $this->option('routing-key') diff --git a/src/Console/QueueDeclareCommand.php b/src/Console/QueueDeclareCommand.php index 6dc1cae6..418d6221 100644 --- a/src/Console/QueueDeclareCommand.php +++ b/src/Console/QueueDeclareCommand.php @@ -4,8 +4,6 @@ use Exception; use Illuminate\Console\Command; -use PhpAmqpLib\Channel\AMQPChannel; -use PhpAmqpLib\Exception\AMQPProtocolChannelException; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Connectors\RabbitMQConnector; class QueueDeclareCommand extends Command @@ -13,8 +11,8 @@ class QueueDeclareCommand extends Command protected $signature = 'rabbitmq:queue-declare {name : The name of the queue to declare} {connection=rabbitmq : The name of the queue connection to use} - {--durable} - {--auto-delete}'; + {--durable=1} + {--auto-delete=0}'; protected $description = ''; @@ -28,34 +26,16 @@ public function handle(RabbitMQConnector $connector): void $queue = $connector->connect($config); - $channel = $queue->getChannel(); + if ($queue->isQueueExists($this->argument('name'))) { + $this->warn('Queue already exists.'); - try { - $channel->queue_declare($this->argument('name'), true); - } catch (AMQPProtocolChannelException $exception) { - if ($exception->amqp_reply_code === 404) { - $this->declareQueue($queue->getConnection()->channel()); - - $this->info('Queue declared successfully.'); - - return; - } - - throw $exception; + return; } - $this->warn('Queue already exists.'); - } - - protected function declareQueue(AMQPChannel $channel): void - { - $channel->queue_declare( + $queue->declareQueue( $this->argument('name'), - false, - $this->option('durable'), - false, - $this->option('auto-delete'), - false + (bool)$this->option('durable'), + (bool)$this->option('auto-delete') ); } } diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index c82acfc2..55a6b490 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -262,6 +262,78 @@ public function getQueue($queue = null) return $queue ?: $this->default; } + /** + * @param string $exchange + * @return bool + * @throws AMQPProtocolChannelException + */ + public function isExchangeExists(string $exchange): bool + { + try { + $this->channel->exchange_declare($exchange, '', true); + + return true; + } catch (AMQPProtocolChannelException $exception) { + if ($exception->amqp_reply_code === 404) { + return false; + } + + throw $exception; + } + } + + public function declareExchange( + string $name, + string $type = AMQPExchangeType::DIRECT, + bool $durable = true, + bool $autoDelete = false + ): void { + $this->channel->exchange_declare( + $name, + $type, + false, + $durable, + $autoDelete, + false, + true + ); + } + + /** + * @param string $name + * @return bool + * @throws AMQPProtocolChannelException + */ + public function isQueueExists(string $name): bool + { + try { + $this->channel->queue_declare($name, true); + + return true; + } catch (AMQPProtocolChannelException $exception) { + if ($exception->amqp_reply_code === 404) { + return false; + } + + throw $exception; + } + } + + public function declareQueue(string $name, bool $durable = true, bool $autoDelete = false): void + { + $this->channel->queue_declare( + $name, + false, + $durable, + $autoDelete + ); + } + + public function bindQueue(string $queue, string $exchange, string $routingKey = ''): void + { + $this->channel->queue_bind($queue, $exchange, $routingKey); + } + public function purge($queue = null): void { $this->channel->queue_purge($this->getQueue($queue)); From 27f123b0463f0b264d6908fd7a634dfbc07dad07 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Mon, 25 Nov 2019 18:28:29 +0300 Subject: [PATCH 097/375] wip --- src/Console/QueueDeclareCommand.php | 2 +- src/Console/QueuePurgeCommand.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Console/QueueDeclareCommand.php b/src/Console/QueueDeclareCommand.php index 418d6221..2b7139c0 100644 --- a/src/Console/QueueDeclareCommand.php +++ b/src/Console/QueueDeclareCommand.php @@ -14,7 +14,7 @@ class QueueDeclareCommand extends Command {--durable=1} {--auto-delete=0}'; - protected $description = ''; + protected $description = 'Declare queue'; /** * @param RabbitMQConnector $connector diff --git a/src/Console/QueuePurgeCommand.php b/src/Console/QueuePurgeCommand.php index b4a418da..ec4b7a9d 100644 --- a/src/Console/QueuePurgeCommand.php +++ b/src/Console/QueuePurgeCommand.php @@ -12,7 +12,7 @@ class QueuePurgeCommand extends Command {queue} {connection=rabbitmq : The name of the queue connection to use}'; - protected $description = 'Purge queue'; + protected $description = 'Purge all messages in queue'; /** * @param RabbitMQConnector $connector From 59e1d2cc2e50c8be4268e9d50821a146c325bf58 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Mon, 25 Nov 2019 18:37:08 +0300 Subject: [PATCH 098/375] wip --- src/Console/QueuePurgeCommand.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Console/QueuePurgeCommand.php b/src/Console/QueuePurgeCommand.php index ec4b7a9d..235cac76 100644 --- a/src/Console/QueuePurgeCommand.php +++ b/src/Console/QueuePurgeCommand.php @@ -4,13 +4,17 @@ use Exception; use Illuminate\Console\Command; +use Illuminate\Console\ConfirmableTrait; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Connectors\RabbitMQConnector; class QueuePurgeCommand extends Command { + use ConfirmableTrait; + protected $signature = 'rabbitmq:queue-purge {queue} - {connection=rabbitmq : The name of the queue connection to use}'; + {connection=rabbitmq : The name of the queue connection to use} + {--force : Force the operation to run when in production}'; protected $description = 'Purge all messages in queue'; @@ -20,6 +24,10 @@ class QueuePurgeCommand extends Command */ public function handle(RabbitMQConnector $connector): void { + if (!$this->confirmToProceed()) { + return; + } + $config = $this->laravel['config']->get('queue.connections.'.$this->argument('connection')); $queue = $connector->connect($config); From 4918d29b002b8c384e1fabd182a136e070efb6d5 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Mon, 25 Nov 2019 19:08:44 +0300 Subject: [PATCH 099/375] wip --- src/Queue/RabbitMQQueue.php | 127 ++++++++++++------------------------ 1 file changed, 41 insertions(+), 86 deletions(-) diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index 55a6b490..6788df70 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -37,12 +37,19 @@ class RabbitMQQueue extends Queue implements QueueContract */ protected $default; + /** + * List of already declared exchanges. + * + * @var array + */ + protected $exchanges = []; + /** * List of already declared queues. * * @var array */ - protected $queues; + protected $queues = []; public function __construct( AbstractConnection $connection, @@ -62,18 +69,7 @@ public function size($queue = null): int // TODO count delayed too $queue = $this->getQueue($queue); - [, $size] = $this->channel->queue_declare( - $queue, - false, - true, - false, - false, - false, - new AMQPTable([ - 'x-dead-letter-exchange' => $queue, - 'x-dead-letter-routing-key' => $queue, - ]) - ); + [, $size] = $this->channel->queue_declare($queue, true); return $size; } @@ -93,30 +89,12 @@ public function pushRaw($payload, $queue = null, array $options = []) { $queue = $this->getQueue($queue); - if (!isset($this->queues[$queue])) { - $this->channel->exchange_declare( - $queue, - AMQPExchangeType::DIRECT, - false, - true, - true - ); - - $this->channel->queue_declare( - $queue, - false, - true, - false, - false, - false, - new AMQPTable([ - 'x-dead-letter-exchange' => $queue, - 'x-dead-letter-routing-key' => $queue, - ]) - ); - - $this->channel->queue_bind($queue, $queue, $queue); - } + $this->declareExchange($queue); + $this->declareQueue($queue, true, false, [ + 'x-dead-letter-exchange' => $queue, + 'x-dead-letter-routing-key' => $queue, + ]); + $this->bindQueue($queue, $queue, $queue); [$message, $correlationId] = $this->createMessage($payload); @@ -145,29 +123,13 @@ public function laterRaw($delay, $payload, $queue = null, $attempts = 0) $destinationQueue = $this->getQueue($queue); $delayedQueue = $this->getQueue($queue).'.delay.'.$ttl; - $this->channel->exchange_declare( - $destinationQueue, - AMQPExchangeType::DIRECT, - false, - true, - true - ); - - $this->channel->queue_declare( - $delayedQueue, - false, - true, - false, - false, - false, - new AMQPTable([ - 'x-dead-letter-exchange' => $destinationQueue, - 'x-dead-letter-routing-key' => $destinationQueue, - 'x-message-ttl' => $ttl, // TODO - ]) - ); - - $this->channel->queue_bind($destinationQueue, $destinationQueue, $destinationQueue); + $this->declareExchange($destinationQueue); + $this->declareQueue($delayedQueue, true, false, [ + 'x-dead-letter-exchange' => $destinationQueue, + 'x-dead-letter-routing-key' => $destinationQueue, + 'x-message-ttl' => $ttl, // TODO + ]); + $this->bindQueue($destinationQueue, $destinationQueue, $destinationQueue); [$message, $correlationId] = $this->createMessage($payload, $attempts); @@ -189,30 +151,12 @@ public function bulk($jobs, $data = '', $queue = null): void $this->createPayload($job, $queue, $data) ); - if (!isset($this->queues[$queue])) { - $this->channel->exchange_declare( - $queue, - AMQPExchangeType::DIRECT, - false, - true, - true - ); - - $this->channel->queue_declare( - $queue, - false, - true, - false, - false, - false, - new AMQPTable([ - 'x-dead-letter-exchange' => $queue, - 'x-dead-letter-routing-key' => $queue, - ]) - ); - - $this->channel->queue_bind($queue, $queue, $queue); - } + $this->declareExchange($queue); + $this->declareQueue($queue, true, false, [ + 'x-dead-letter-exchange' => $queue, + 'x-dead-letter-routing-key' => $queue, + ]); + $this->bindQueue($queue, $queue, $queue); $this->channel->batch_basic_publish($message); } @@ -288,6 +232,10 @@ public function declareExchange( bool $durable = true, bool $autoDelete = false ): void { + if (in_array($name, $this->exchanges, true)) { + return; + } + $this->channel->exchange_declare( $name, $type, @@ -319,13 +267,20 @@ public function isQueueExists(string $name): bool } } - public function declareQueue(string $name, bool $durable = true, bool $autoDelete = false): void + public function declareQueue(string $name, bool $durable = true, bool $autoDelete = false, array $arguments = []): void { + if (in_array($name, $this->queues, true)) { + return; + } + $this->channel->queue_declare( $name, false, $durable, - $autoDelete + $autoDelete, + false, + false, + new AMQPTable($arguments) ); } From 2016526890a78c706555d1b935006ac33122d7df Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Mon, 25 Nov 2019 19:15:04 +0300 Subject: [PATCH 100/375] wip --- src/Queue/RabbitMQQueue.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index 6788df70..812dffa3 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -51,6 +51,13 @@ class RabbitMQQueue extends Queue implements QueueContract */ protected $queues = []; + /** + * List of already bound queues to exchanges. + * + * @var array + */ + protected $boundQueues = []; + public function __construct( AbstractConnection $connection, AMQPChannel $channel, @@ -286,6 +293,14 @@ public function declareQueue(string $name, bool $durable = true, bool $autoDelet public function bindQueue(string $queue, string $exchange, string $routingKey = ''): void { + if (in_array( + implode('', compact('queue', 'exchange', 'routingKey')), + $this->boundQueues, + true + )) { + return; + } + $this->channel->queue_bind($queue, $exchange, $routingKey); } From a622a336653b90355432d68bd30b25dd65491b95 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Mon, 25 Nov 2019 19:15:35 +0300 Subject: [PATCH 101/375] wip --- src/Queue/RabbitMQQueue.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index 812dffa3..dfe46104 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -1,5 +1,7 @@ Date: Mon, 25 Nov 2019 19:16:38 +0300 Subject: [PATCH 102/375] wip --- src/Queue/RabbitMQQueue.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index dfe46104..3ecba4fc 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -286,8 +286,8 @@ public function declareQueue(string $name, bool $durable = true, bool $autoDelet $name, false, $durable, - $autoDelete, false, + $autoDelete, false, new AMQPTable($arguments) ); From b78c79af3878a77152733638d73d764afb947c9f Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Mon, 25 Nov 2019 20:31:52 +0300 Subject: [PATCH 103/375] Update issue templates --- .github/ISSUE_TEMPLATE/bug_report.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..dea6573b --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,22 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: vyuldashev + +--- + +- Laravel Version: #.#.# +- Package Version: #.#.# +- RabbitMQ Version: #.#.# + +**Describe the bug** +A clear and concise description of what the bug is. + +**Steps To Reproduce** + +**Expected behavior** + +**Additional context** +Add any other context about the problem here. From d4aa9639b39334365b5477fde00f13d97262e099 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Mon, 25 Nov 2019 21:05:21 +0300 Subject: [PATCH 104/375] wip --- src/Queue/RabbitMQQueue.php | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index 3ecba4fc..3376ff5b 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -72,13 +72,22 @@ public function __construct( /** * {@inheritdoc} + * + * @throws AMQPProtocolChannelException */ public function size($queue = null): int { // TODO count delayed too $queue = $this->getQueue($queue); - [, $size] = $this->channel->queue_declare($queue, true); + if (!$this->isQueueExists($queue)) { + return 0; + } + + // create a temporary channel, so the main channel will not be closed on exception + $channel = $this->connection->channel(); + [, $size] = $channel->queue_declare($queue, true); + $channel->close(); return $size; } @@ -129,10 +138,18 @@ public function laterRaw($delay, $payload, $queue = null, $attempts = 0) { $ttl = $this->secondsUntil($delay) * 1000; + if($ttl < 0) { + return $this->pushRaw($payload, $queue, []); + } + $destinationQueue = $this->getQueue($queue); $delayedQueue = $this->getQueue($queue).'.delay.'.$ttl; $this->declareExchange($destinationQueue); + $this->declareQueue($destinationQueue, true, false, [ + 'x-dead-letter-exchange' => $destinationQueue, + 'x-dead-letter-routing-key' => $destinationQueue, + ]); $this->declareQueue($delayedQueue, true, false, [ 'x-dead-letter-exchange' => $destinationQueue, 'x-dead-letter-routing-key' => $destinationQueue, @@ -223,7 +240,10 @@ public function getQueue($queue = null) public function isExchangeExists(string $exchange): bool { try { - $this->channel->exchange_declare($exchange, '', true); + // create a temporary channel, so the main channel will not be closed on exception + $channel = $this->connection->channel(); + $channel->exchange_declare($exchange, '', true); + $channel->close(); return true; } catch (AMQPProtocolChannelException $exception) { @@ -261,10 +281,15 @@ public function declareExchange( * @return bool * @throws AMQPProtocolChannelException */ - public function isQueueExists(string $name): bool + public function isQueueExists(?string $name = null): bool { try { - $this->channel->queue_declare($name, true); + $name = $this->getQueue($name); + + // create a temporary channel, so the main channel will not be closed on exception + $channel = $this->connection->channel(); + $channel->queue_declare($name, true); + $channel->close(); return true; } catch (AMQPProtocolChannelException $exception) { From 427e303b05455a4c99747a55ed5214ce4c2c21ee Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Mon, 25 Nov 2019 21:06:29 +0300 Subject: [PATCH 105/375] wip --- tests/Feature/TestCase.php | 53 ++++++++------------------------------ tests/Mocks/TestJob.php | 7 +++++ 2 files changed, 18 insertions(+), 42 deletions(-) diff --git a/tests/Feature/TestCase.php b/tests/Feature/TestCase.php index 75331305..7577567e 100644 --- a/tests/Feature/TestCase.php +++ b/tests/Feature/TestCase.php @@ -4,20 +4,28 @@ use Illuminate\Support\Facades\Queue; use Illuminate\Support\Str; +use PhpAmqpLib\Exception\AMQPProtocolChannelException; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Jobs\RabbitMQJob; use VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Mocks\TestJob; use VladimirYuldashev\LaravelQueueRabbitMQ\Tests\TestCase as BaseTestCase; -// https://github.com/laravel/framework/blob/6.x/tests/Queue/RedisQueueIntegrationTest.php abstract class TestCase extends BaseTestCase { + /** + * @throws AMQPProtocolChannelException + */ protected function setUp(): void { parent::setUp(); - Queue::size(); + if($this->connection()->isQueueExists()) { + $this->connection()->purge(); + } + } - $this->connection()->purge(); + public function testSizeDoesNotThrowExceptionOnUnknownQueue(): void + { + $this->assertEmpty(0, Queue::size(Str::random())); } public function testPop(): void @@ -234,43 +242,4 @@ public function testReleaseAndReleaseWithDelayAttempts(): void $this->assertNotNull($job = Queue::pop()); $this->assertSame(2, $job->attempts()); } - -// public function testFoo() -// { -// /** @var RabbitMQJob $job */ -// Queue::pushRaw('foo'); -// -// $job = Queue::pop(); -// -// $this->assertNotNull($job); -// -// $job->release(); -// -// sleep(1); -// -// $job = Queue::pop(); -// -// $this->assertNotNull($job); -// -// $this->connection()->getChannel()->basic_reject($job->getMessage()->getDeliveryTag(), false); -// $this->connection()->getChannel()->wait_for_pending_acks_returns(); -// -// sleep(1); -// -// $job = Queue::pop(); -// -// sleep(1); -// -// $this->connection()->getChannel()->basic_nack($job->getMessage()->getDeliveryTag(), false, false); -// $this->connection()->getChannel()->wait_for_pending_acks_returns(); -// -// sleep(1); -// -// $job = Queue::pop(); -// -// /** @var AMQPTable|null $headers */ -// $headers = Arr::get($job->getMessage()->get_properties(), 'application_headers'); -// -// dd($headers->getNativeData()); -// } } diff --git a/tests/Mocks/TestJob.php b/tests/Mocks/TestJob.php index 9e70b446..0f290fd4 100644 --- a/tests/Mocks/TestJob.php +++ b/tests/Mocks/TestJob.php @@ -6,6 +6,13 @@ class TestJob implements ShouldQueue { + public $i; + + public function __construct($i = 0) + { + $this->i = $i; + } + public function handle(): void { // From 691ac7fbbcb580fa71243369259d495d0910b3f3 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Tue, 26 Nov 2019 01:31:37 +0300 Subject: [PATCH 106/375] wip --- src/Horizon/RabbitMQQueue.php | 4 +- src/Queue/Connectors/RabbitMQConnector.php | 41 +++-- src/Queue/RabbitMQQueue.php | 23 ++- tests/Feature/TestCase.php | 195 +++++++++++++-------- 4 files changed, 165 insertions(+), 98 deletions(-) diff --git a/src/Horizon/RabbitMQQueue.php b/src/Horizon/RabbitMQQueue.php index e8f99036..d8f7f898 100644 --- a/src/Horizon/RabbitMQQueue.php +++ b/src/Horizon/RabbitMQQueue.php @@ -9,6 +9,7 @@ use Laravel\Horizon\Events\JobReserved; use Laravel\Horizon\JobId; use Laravel\Horizon\JobPayload; +use PhpAmqpLib\Exception\AMQPProtocolChannelException; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Jobs\RabbitMQJob; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue as BaseRabbitMQQueue; @@ -24,8 +25,9 @@ class RabbitMQQueue extends BaseRabbitMQQueue /** * Get the number of queue jobs that are ready to process. * - * @param string|null $queue + * @param string|null $queue * @return int + * @throws AMQPProtocolChannelException */ public function readyNow($queue = null): int { diff --git a/src/Queue/Connectors/RabbitMQConnector.php b/src/Queue/Connectors/RabbitMQConnector.php index 02b201ad..c9838d53 100644 --- a/src/Queue/Connectors/RabbitMQConnector.php +++ b/src/Queue/Connectors/RabbitMQConnector.php @@ -39,30 +39,27 @@ public function __construct(Dispatcher $dispatcher) public function connect(array $config): Queue { $connection = $this->createConnection($config); - $channel = $connection->channel(); - $this->dispatcher->listen(WorkerStopping::class, static function () use ($channel, $connection): void { - $channel->close(); - $connection->close(); - }); - - $worker = Arr::get($config, 'worker', 'default'); + $queue = $this->createQueue( + Arr::get($config, 'worker', 'default'), + $connection, + $config['queue'] + ); - if ($worker === 'default') { - return new RabbitMQQueue($connection, $channel, $config['queue']); + if(!$queue instanceof RabbitMQQueue) { + throw new InvalidArgumentException('Invalid worker.'); } - if ($worker === 'horizon') { + if($queue instanceof HorizonRabbitMQQueue) { $this->dispatcher->listen(JobFailed::class, RabbitMQFailedEvent::class); - - return new HorizonRabbitMQQueue($connection, $channel, $config['queue']); } - if ($worker instanceof RabbitMQQueue) { - return new $worker($connection, $channel, $config); - } + $this->dispatcher->listen(WorkerStopping::class, static function () use ($queue): void { + $queue->close(); + }); - throw new InvalidArgumentException('Invalid worker.'); + + return $queue; } /** @@ -81,6 +78,18 @@ protected function createConnection(array $config): AbstractConnection ); } + protected function createQueue(string $worker, AbstractConnection $connection, string $queue) + { + switch($worker) { + case 'default': + return new RabbitMQQueue($connection, $queue); + case 'horizon': + return new HorizonRabbitMQQueue($connection, $queue); + default: + return new $worker($connection, $queue); + } + } + private function filter(array $array): array { foreach ($array as $index => &$value) { diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index 3376ff5b..5481ca2d 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -62,11 +62,10 @@ class RabbitMQQueue extends Queue implements QueueContract public function __construct( AbstractConnection $connection, - AMQPChannel $channel, string $default ) { $this->connection = $connection; - $this->channel = $channel; + $this->channel = $connection->channel(); $this->default = $default; } @@ -184,7 +183,7 @@ public function bulk($jobs, $data = '', $queue = null): void ]); $this->bindQueue($queue, $queue, $queue); - $this->channel->batch_basic_publish($message); + $this->channel->batch_basic_publish($message, $queue, $queue); } $this->channel->publish_batch(); @@ -333,8 +332,10 @@ public function bindQueue(string $queue, string $exchange, string $routingKey = public function purge($queue = null): void { - $this->channel->queue_purge($this->getQueue($queue)); - $this->channel->wait_for_pending_acks(); + // create a temporary channel, so the main channel will not be closed on exception + $channel = $this->connection->channel(); + $channel->queue_purge($this->getQueue($queue)); + $channel->close(); } public function ack(RabbitMQJob $job): void @@ -343,12 +344,20 @@ public function ack(RabbitMQJob $job): void $this->channel->wait_for_pending_acks(); } - public function reject(RabbitMQJob $job): void + public function reject(RabbitMQJob $job, bool $requeue = false): void { - $this->channel->basic_reject($job->getRabbitMQMessage()->getDeliveryTag(), false); + $this->channel->basic_reject($job->getRabbitMQMessage()->getDeliveryTag(), $requeue); $this->channel->wait_for_pending_acks(); } + /** + * @throws Exception + */ + public function close(): void + { + $this->connection->close(); + } + protected function createMessage($payload, int $attempts = 0): array { $properties = [ diff --git a/tests/Feature/TestCase.php b/tests/Feature/TestCase.php index 7577567e..b5909ca5 100644 --- a/tests/Feature/TestCase.php +++ b/tests/Feature/TestCase.php @@ -23,20 +23,32 @@ protected function setUp(): void } } + /** + * @throws AMQPProtocolChannelException + */ + protected function tearDown(): void + { + if($this->connection()->isQueueExists()) { + $this->connection()->purge(); + } + + $this->assertSame(0, Queue::size()); + + parent::tearDown(); + } + public function testSizeDoesNotThrowExceptionOnUnknownQueue(): void { $this->assertEmpty(0, Queue::size(Str::random())); } - public function testPop(): void + public function testPopNothing(): void { $this->assertNull(Queue::pop('foo')); } public function testPushRaw(): void { - $this->assertSame(0, Queue::size()); - Queue::pushRaw($payload = Str::random()); sleep(1); @@ -54,67 +66,64 @@ public function testPushRaw(): void $this->assertSame(0, Queue::size()); } - public function testReleaseRaw(): void + public function testPush(): void { - Queue::pushRaw($payload = Str::random()); + Queue::push(new TestJob()); sleep(1); $this->assertSame(1, Queue::size()); $this->assertNotNull($job = Queue::pop()); $this->assertSame(0, $job->attempts()); + $this->assertInstanceOf(RabbitMQJob::class, $job); + $this->assertSame(TestJob::class, $job->resolveName()); + $this->assertNotNull($job->getJobId()); - for ($attempt = 1; $attempt <= 3; $attempt++) { - $job->release(); - - sleep(1); // TODO ??? - - $this->assertSame(1, Queue::size()); - - $job = Queue::pop(); + $payload = $job->payload(); - $this->assertSame($attempt, $job->attempts()); - } + $this->assertSame(TestJob::class, $payload['displayName']); + $this->assertSame('Illuminate\Queue\CallQueuedHandler@call', $payload['job']); + $this->assertNull($payload['maxTries']); + $this->assertNull($payload['delay']); + $this->assertNull($payload['timeout']); + $this->assertNull($payload['timeoutAt']); + $this->assertSame($job->getJobId(), $payload['id']); } - public function testReleaseWithDelayRaw(): void + public function testLaterRaw(): void { - $this->assertSame(0, Queue::size()); + $payload = Str::random(); + $data = [Str::random() => Str::random()]; - Queue::pushRaw($payload = Str::random()); + Queue::later(3, $payload, $data); sleep(1); - $this->assertSame(1, Queue::size()); - $this->assertNotNull($job = Queue::pop()); - $this->assertSame(0, $job->attempts()); + $this->assertSame(0, Queue::size()); + $this->assertNull(Queue::pop()); - for ($attempt = 1; $attempt <= 3; $attempt++) { - $job->release(4); + sleep(3); - sleep(1); // TODO ??? + $this->assertSame(1, Queue::size()); + $this->assertNotNull($job = Queue::pop()); - $this->assertSame(0, Queue::size()); - $this->assertNull(Queue::pop()); + $this->assertInstanceOf(RabbitMQJob::class, $job); + $this->assertSame($payload, $job->getName()); - sleep(4); + $body = json_decode($job->getRawBody(), true); - $this->assertSame(1, Queue::size()); + $this->assertSame($payload, $body['displayName']); + $this->assertSame($payload, $body['job']); + $this->assertSame($data, $body['data']); - $job = Queue::pop(); + $job->delete(); - $this->assertSame($attempt, $job->attempts()); - } + $this->assertSame(0, Queue::size()); } - public function testLaterRaw(): void + public function testLater(): void { - $this->assertSame(0, Queue::size()); - - $payload = Str::random(); - $data = [Str::random() => Str::random()]; - - Queue::later(3, $payload, $data); + Queue::later(3, new TestJob()); sleep(1); @@ -127,41 +136,56 @@ public function testLaterRaw(): void $this->assertNotNull($job = Queue::pop()); $this->assertInstanceOf(RabbitMQJob::class, $job); - $this->assertSame($payload, $job->getName()); $body = json_decode($job->getRawBody(), true); - $this->assertSame($payload, $body['displayName']); - $this->assertSame($payload, $body['job']); - $this->assertSame($data, $body['data']); + $this->assertSame(TestJob::class, $body['displayName']); + $this->assertSame('Illuminate\Queue\CallQueuedHandler@call', $body['job']); + $this->assertSame(TestJob::class, $body['data']['commandName']); + $this->assertNotNull($job->getJobId()); $job->delete(); $this->assertSame(0, Queue::size()); } - public function testPush(): void + public function testBulk(): void { - Queue::push(new TestJob()); + $count = 100; + $jobs = []; + + for($i = 0; $i < $count; $i++) { + $jobs[$i] = new TestJob($i); + } + + Queue::bulk($jobs); + + sleep(1); + + $this->assertSame($count, Queue::size()); + } + + public function testReleaseRaw(): void + { + Queue::pushRaw($payload = Str::random()); sleep(1); $this->assertSame(1, Queue::size()); $this->assertNotNull($job = Queue::pop()); $this->assertSame(0, $job->attempts()); - $this->assertInstanceOf(RabbitMQJob::class, $job); - $this->assertSame(TestJob::class, $job->resolveName()); - $this->assertNotNull($job->getJobId()); - $payload = $job->payload(); + for ($attempt = 1; $attempt <= 3; $attempt++) { + $job->release(); - $this->assertSame(TestJob::class, $payload['displayName']); - $this->assertSame('Illuminate\Queue\CallQueuedHandler@call', $payload['job']); - $this->assertNull($payload['maxTries']); - $this->assertNull($payload['delay']); - $this->assertNull($payload['timeout']); - $this->assertNull($payload['timeoutAt']); - $this->assertSame($job->getJobId(), $payload['id']); + sleep(1); // TODO ??? + + $this->assertSame(1, Queue::size()); + + $job = Queue::pop(); + + $this->assertSame($attempt, $job->attempts()); + } } public function testRelease(): void @@ -187,40 +211,48 @@ public function testRelease(): void } } - public function testLater(): void + public function testReleaseWithDelayRaw(): void { - $this->assertSame(0, Queue::size()); - - Queue::later(3, new TestJob()); + Queue::pushRaw($payload = Str::random()); sleep(1); - $this->assertSame(0, Queue::size()); - $this->assertNull(Queue::pop()); - - sleep(3); - $this->assertSame(1, Queue::size()); $this->assertNotNull($job = Queue::pop()); + $this->assertSame(0, $job->attempts()); - $this->assertInstanceOf(RabbitMQJob::class, $job); + for ($attempt = 1; $attempt <= 3; $attempt++) { + $job->release(4); - $body = json_decode($job->getRawBody(), true); + sleep(1); // TODO ??? - $this->assertSame(TestJob::class, $body['displayName']); - $this->assertSame('Illuminate\Queue\CallQueuedHandler@call', $body['job']); - $this->assertSame(TestJob::class, $body['data']['commandName']); - $this->assertNotNull($job->getJobId()); + $this->assertSame(0, Queue::size()); + $this->assertNull(Queue::pop()); - $job->delete(); + sleep(4); - $this->assertSame(0, Queue::size()); + $this->assertSame(1, Queue::size()); + + $job = Queue::pop(); + + $this->assertSame($attempt, $job->attempts()); + } } - public function testReleaseAndReleaseWithDelayAttempts(): void + public function testReleaseInThePast(): void { - $this->assertSame(0, Queue::size()); + Queue::push(new TestJob()); + + $job = Queue::pop(); + $job->release(-3); + sleep(1); + + $this->assertInstanceOf(RabbitMQJob::class, Queue::pop()); + } + + public function testReleaseAndReleaseWithDelayAttempts(): void + { Queue::push(new TestJob()); sleep(1); @@ -240,6 +272,21 @@ public function testReleaseAndReleaseWithDelayAttempts(): void sleep(4); $this->assertNotNull($job = Queue::pop()); + $this->assertSame(2, $job->attempts()); } + + public function testDelete(): void + { + Queue::push(new TestJob()); + + $job = Queue::pop(); + + $job->delete(); + + sleep(1); + + $this->assertSame(0, Queue::size()); + $this->assertNull(Queue::pop()); + } } From fd28a48001a7e4cbbde45acdfc9302b009d3ef46 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Tue, 26 Nov 2019 01:33:19 +0300 Subject: [PATCH 107/375] wip --- src/Queue/RabbitMQQueue.php | 5 ----- tests/Feature/TestCase.php | 6 +++--- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index 5481ca2d..f857b738 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -76,7 +76,6 @@ public function __construct( */ public function size($queue = null): int { - // TODO count delayed too $queue = $this->getQueue($queue); if (!$this->isQueueExists($queue)) { @@ -116,7 +115,6 @@ public function pushRaw($payload, $queue = null, array $options = []) [$message, $correlationId] = $this->createMessage($payload); $this->channel->basic_publish($message, $queue, $queue, true, false); - $this->channel->wait_for_pending_acks(); return $correlationId; } @@ -159,7 +157,6 @@ public function laterRaw($delay, $payload, $queue = null, $attempts = 0) [$message, $correlationId] = $this->createMessage($payload, $attempts); $this->channel->basic_publish($message, null, $delayedQueue, true, false); - $this->channel->wait_for_pending_acks(); return $correlationId; } @@ -341,13 +338,11 @@ public function purge($queue = null): void public function ack(RabbitMQJob $job): void { $this->channel->basic_ack($job->getRabbitMQMessage()->getDeliveryTag()); - $this->channel->wait_for_pending_acks(); } public function reject(RabbitMQJob $job, bool $requeue = false): void { $this->channel->basic_reject($job->getRabbitMQMessage()->getDeliveryTag(), $requeue); - $this->channel->wait_for_pending_acks(); } /** diff --git a/tests/Feature/TestCase.php b/tests/Feature/TestCase.php index b5909ca5..c3dc3dc6 100644 --- a/tests/Feature/TestCase.php +++ b/tests/Feature/TestCase.php @@ -178,7 +178,7 @@ public function testReleaseRaw(): void for ($attempt = 1; $attempt <= 3; $attempt++) { $job->release(); - sleep(1); // TODO ??? + sleep(1); $this->assertSame(1, Queue::size()); @@ -201,7 +201,7 @@ public function testRelease(): void for ($attempt = 1; $attempt <= 3; $attempt++) { $job->release(); - sleep(1); // TODO ??? + sleep(1); $this->assertSame(1, Queue::size()); @@ -224,7 +224,7 @@ public function testReleaseWithDelayRaw(): void for ($attempt = 1; $attempt <= 3; $attempt++) { $job->release(4); - sleep(1); // TODO ??? + sleep(1); $this->assertSame(0, Queue::size()); $this->assertNull(Queue::pop()); From e335d7e61849b88a441dd077ef7d7cc3ccbe0a65 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Tue, 26 Nov 2019 01:33:58 +0300 Subject: [PATCH 108/375] wip --- src/Queue/Connectors/RabbitMQConnector.php | 6 +++--- src/Queue/RabbitMQQueue.php | 2 +- tests/Feature/TestCase.php | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Queue/Connectors/RabbitMQConnector.php b/src/Queue/Connectors/RabbitMQConnector.php index c9838d53..e2683aef 100644 --- a/src/Queue/Connectors/RabbitMQConnector.php +++ b/src/Queue/Connectors/RabbitMQConnector.php @@ -46,11 +46,11 @@ public function connect(array $config): Queue $config['queue'] ); - if(!$queue instanceof RabbitMQQueue) { + if (!$queue instanceof RabbitMQQueue) { throw new InvalidArgumentException('Invalid worker.'); } - if($queue instanceof HorizonRabbitMQQueue) { + if ($queue instanceof HorizonRabbitMQQueue) { $this->dispatcher->listen(JobFailed::class, RabbitMQFailedEvent::class); } @@ -80,7 +80,7 @@ protected function createConnection(array $config): AbstractConnection protected function createQueue(string $worker, AbstractConnection $connection, string $queue) { - switch($worker) { + switch ($worker) { case 'default': return new RabbitMQQueue($connection, $queue); case 'horizon': diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index f857b738..961b5d72 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -135,7 +135,7 @@ public function laterRaw($delay, $payload, $queue = null, $attempts = 0) { $ttl = $this->secondsUntil($delay) * 1000; - if($ttl < 0) { + if ($ttl < 0) { return $this->pushRaw($payload, $queue, []); } diff --git a/tests/Feature/TestCase.php b/tests/Feature/TestCase.php index c3dc3dc6..60f39b4c 100644 --- a/tests/Feature/TestCase.php +++ b/tests/Feature/TestCase.php @@ -18,7 +18,7 @@ protected function setUp(): void { parent::setUp(); - if($this->connection()->isQueueExists()) { + if ($this->connection()->isQueueExists()) { $this->connection()->purge(); } } @@ -28,7 +28,7 @@ protected function setUp(): void */ protected function tearDown(): void { - if($this->connection()->isQueueExists()) { + if ($this->connection()->isQueueExists()) { $this->connection()->purge(); } @@ -154,7 +154,7 @@ public function testBulk(): void $count = 100; $jobs = []; - for($i = 0; $i < $count; $i++) { + for ($i = 0; $i < $count; $i++) { $jobs[$i] = new TestJob($i); } From e93982344b3babd40f2d751255f128755b96d095 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Tue, 26 Nov 2019 01:59:44 +0300 Subject: [PATCH 109/375] wip --- src/Horizon/RabbitMQQueue.php | 12 ++++++------ src/Queue/RabbitMQQueue.php | 3 +-- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/Horizon/RabbitMQQueue.php b/src/Horizon/RabbitMQQueue.php index d8f7f898..a3577114 100644 --- a/src/Horizon/RabbitMQQueue.php +++ b/src/Horizon/RabbitMQQueue.php @@ -59,12 +59,12 @@ public function pushRaw($payload, $queue = null, array $options = []) /** * {@inheritdoc} */ - public function later($delay, $job, $data = '', $queueName = null) + public function later($delay, $job, $data = '', $queue = null) { $payload = (new JobPayload($this->createPayload($job, $data)))->prepare($job)->value; - return tap(parent::pushRaw($payload, $queueName, ['delay' => $this->secondsUntil($delay)]), function () use ($payload, $queueName): void { - $this->event($this->getQueue($queueName), new JobPushed($payload)); + return tap(parent::pushRaw($payload, $queue, ['delay' => $this->secondsUntil($delay)]), function () use ($payload, $queue): void { + $this->event($this->getQueue($queue), new JobPushed($payload)); }); } @@ -93,14 +93,14 @@ public function release($delay, $job, $data, $queue, $attempts = 0) /** * Fire the job deleted event. * - * @param string $queueName + * @param string $queue * @param RabbitMQJob $job * @return void * @throws BindingResolutionException */ - public function deleteReserved($queueName, $job): void + public function deleteReserved($queue, $job): void { - $this->event($this->getQueue($queueName), new JobDeleted($job, $job->getRawBody())); + $this->event($this->getQueue($queue), new JobDeleted($job, $job->getRawBody())); } /** diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index 961b5d72..40a45faf 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -150,7 +150,7 @@ public function laterRaw($delay, $payload, $queue = null, $attempts = 0) $this->declareQueue($delayedQueue, true, false, [ 'x-dead-letter-exchange' => $destinationQueue, 'x-dead-letter-routing-key' => $destinationQueue, - 'x-message-ttl' => $ttl, // TODO + 'x-message-ttl' => $ttl, ]); $this->bindQueue($destinationQueue, $destinationQueue, $destinationQueue); @@ -360,7 +360,6 @@ protected function createMessage($payload, int $attempts = 0): array 'delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT, ]; - // todo if ($correlationId = json_decode($payload, true)['id'] ?? null) { $properties['correlation_id'] = $correlationId; } From 5b8eac587ba151b85a58a207bc0f5311c38b48cb Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Tue, 26 Nov 2019 12:13:02 +0300 Subject: [PATCH 110/375] update readme --- README.md | 136 ++++++++++++++---------------------------------------- 1 file changed, 35 insertions(+), 101 deletions(-) diff --git a/README.md b/README.md index 8e4d0d0f..bde9b853 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,17 @@ RabbitMQ Queue driver for Laravel [![StyleCI](https://styleci.io/repos/14976752/shield)](https://styleci.io/repos/14976752) [![License](https://poser.pugx.org/vladimir-yuldashev/laravel-queue-rabbitmq/license?format=flat-square)](https://packagist.org/packages/vladimir-yuldashev/laravel-queue-rabbitmq) +## Support Policy + +| Package Version | Laravel Version | Bug Fixes Until | +|-----------------|-----------------|---------------------| +| 6.0 | 5.5 | August 30th, 2019 | +| 7.0 | 5.6 | August 7th, 2018 | +| 7.1 | 5.7 | March 4th, 2019 | +| 7.2 | 5.8 | August 26th, 2019 | +| 9 | 6 | September 3rd, 2021 | +| 10 | 6 | September 3rd, 2021 | + ## Installation You can install this package via composer using this command: @@ -21,106 +32,40 @@ Setup connection in `config/queue.php` ```php 'connections' => [ // ... + 'rabbitmq' => [ 'driver' => 'rabbitmq', - - /* - * Set to "horizon" if you wish to use Laravel Horizon. - */ - 'worker' => env('RABBITMQ_WORKER', 'default'), - - 'dsn' => env('RABBITMQ_DSN', null), - - /* - * Could be one a class that implements \Interop\Amqp\AmqpConnectionFactory for example: - * - \EnqueueAmqpExt\AmqpConnectionFactory if you install enqueue/amqp-ext - * - \EnqueueAmqpLib\AmqpConnectionFactory if you install enqueue/amqp-lib - * - \EnqueueAmqpBunny\AmqpConnectionFactory if you install enqueue/amqp-bunny - */ - - 'factory_class' => Enqueue\AmqpLib\AmqpConnectionFactory::class, - - 'host' => env('RABBITMQ_HOST', '127.0.0.1'), - 'port' => env('RABBITMQ_PORT', 5672), - - 'vhost' => env('RABBITMQ_VHOST', '/'), - 'login' => env('RABBITMQ_LOGIN', 'guest'), - 'password' => env('RABBITMQ_PASSWORD', 'guest'), - 'queue' => env('RABBITMQ_QUEUE', 'default'), - - 'options' => [ - - 'exchange' => [ - - 'name' => env('RABBITMQ_EXCHANGE_NAME'), - - /* - * Determine if exchange should be created if it does not exist. - */ - - 'declare' => env('RABBITMQ_EXCHANGE_DECLARE', true), - - /* - * Read more about possible values at https://www.rabbitmq.com/tutorials/amqp-concepts.html - */ - - 'type' => env('RABBITMQ_EXCHANGE_TYPE', \Interop\Amqp\AmqpTopic::TYPE_DIRECT), - 'passive' => env('RABBITMQ_EXCHANGE_PASSIVE', false), - 'durable' => env('RABBITMQ_EXCHANGE_DURABLE', true), - 'auto_delete' => env('RABBITMQ_EXCHANGE_AUTODELETE', false), - 'arguments' => env('RABBITMQ_EXCHANGE_ARGUMENTS'), + 'connection' => PhpAmqpLib\Connection\AMQPLazyConnection::class, + + 'hosts' => [ + [ + 'host' => env('RABBITMQ_HOST', '127.0.0.1'), + 'port' => env('RABBITMQ_PORT', 5672), + 'user' => env('RABBITMQ_USER', 'guest'), + 'password' => env('RABBITMQ_PASSWORD', 'guest'), + 'vhost' => env('RABBITMQ_VHOST', '/'), ], - - 'queue' => [ - - /* - * Determine if queue should be created if it does not exist. - */ - - 'declare' => env('RABBITMQ_QUEUE_DECLARE', true), - - /* - * Determine if queue should be binded to the exchange created. - */ - - 'bind' => env('RABBITMQ_QUEUE_DECLARE_BIND', true), - - /* - * Read more about possible values at https://www.rabbitmq.com/tutorials/amqp-concepts.html - */ - - 'passive' => env('RABBITMQ_QUEUE_PASSIVE', false), - 'durable' => env('RABBITMQ_QUEUE_DURABLE', true), - 'exclusive' => env('RABBITMQ_QUEUE_EXCLUSIVE', false), - 'auto_delete' => env('RABBITMQ_QUEUE_AUTODELETE', false), - 'arguments' => env('RABBITMQ_QUEUE_ARGUMENTS'), + ], + + 'options' => [ + 'ssl_options' => [ + 'cafile' => env('RABBITMQ_SSL_CAFILE', null), + 'local_cert' => env('RABBITMQ_SSL_LOCALCERT', null), + 'local_key' => env('RABBITMQ_SSL_LOCALKEY', null), + 'verify_peer' => env('RABBITMQ_SSL_VERIFY_PEER', true), + 'passphrase' => env('RABBITMQ_SSL_PASSPHRASE', null), ], ], - - /* - * Determine the number of seconds to sleep if there's an error communicating with rabbitmq - * If set to false, it'll throw an exception rather than doing the sleep for X seconds. - */ - - 'sleep_on_error' => env('RABBITMQ_ERROR_SLEEP', 5), - + /* - * Optional SSL params if an SSL connection is used - * Using an SSL connection will also require to configure your RabbitMQ to enable SSL. More details can be founds here: https://www.rabbitmq.com/ssl.html - */ - - 'ssl_params' => [ - 'ssl_on' => env('RABBITMQ_SSL', false), - 'cafile' => env('RABBITMQ_SSL_CAFILE', null), - 'local_cert' => env('RABBITMQ_SSL_LOCALCERT', null), - 'local_key' => env('RABBITMQ_SSL_LOCALKEY', null), - 'verify_peer' => env('RABBITMQ_SSL_VERIFY_PEER', true), - 'passphrase' => env('RABBITMQ_SSL_PASSPHRASE', null), - ], + * Set to "horizon" if you wish to use Laravel Horizon. + */ + 'worker' => env('RABBITMQ_WORKER', 'default'), ], + // ... ], ``` @@ -141,17 +86,6 @@ For Lumen usage the service provider should be registered manually as follow in $app->register(VladimirYuldashev\LaravelQueueRabbitMQ\LaravelQueueRabbitMQServiceProvider::class); ``` -## Support Policy - -| Package Version | Laravel Version | Bug Fixes Until | -|-----------------|-----------------|---------------------| -| 6.0 | 5.5 | August 30th, 2019 | -| 7.0 | 5.6 | August 7th, 2018 | -| 7.1 | 5.7 | March 4th, 2019 | -| 7.2 | 5.8 | August 26th, 2019 | -| 9 | 6 | September 3rd, 2021 | -| 10 | 6 | September 3rd, 2021 | - ## Testing Setup RabbitMQ using `docker-compose`: From 9ea209671f0c1b33941a636383ac8d999b370a55 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Tue, 26 Nov 2019 12:41:28 +0300 Subject: [PATCH 111/375] wip --- .php_cs.dist | 4 ++++ src/Console/ExchangeDeclareCommand.php | 4 ++-- src/Console/QueueDeclareCommand.php | 4 ++-- src/Console/QueuePurgeCommand.php | 2 +- src/Horizon/RabbitMQQueue.php | 2 +- src/Queue/Connectors/RabbitMQConnector.php | 2 +- src/Queue/Jobs/RabbitMQJob.php | 8 ++++---- src/Queue/RabbitMQQueue.php | 4 ++-- 8 files changed, 17 insertions(+), 13 deletions(-) diff --git a/.php_cs.dist b/.php_cs.dist index e8f48f80..930c161e 100644 --- a/.php_cs.dist +++ b/.php_cs.dist @@ -50,4 +50,8 @@ return PhpCsFixer\Config::create() ], 'fully_qualified_strict_types' => true, 'void_return' => true, + 'cast_spaces' => [ + 'space' => 'single', + ], + 'not_operator_with_successor_space' => true, ]); diff --git a/src/Console/ExchangeDeclareCommand.php b/src/Console/ExchangeDeclareCommand.php index bc9c2f71..61c52db4 100644 --- a/src/Console/ExchangeDeclareCommand.php +++ b/src/Console/ExchangeDeclareCommand.php @@ -36,8 +36,8 @@ public function handle(RabbitMQConnector $connector): void $queue->declareExchange( $this->argument('name'), $this->argument('type'), - (bool)$this->option('durable'), - (bool)$this->option('auto-delete') + (bool) $this->option('durable'), + (bool) $this->option('auto-delete') ); $this->warn('Exchange already exists.'); diff --git a/src/Console/QueueDeclareCommand.php b/src/Console/QueueDeclareCommand.php index 2b7139c0..c3059668 100644 --- a/src/Console/QueueDeclareCommand.php +++ b/src/Console/QueueDeclareCommand.php @@ -34,8 +34,8 @@ public function handle(RabbitMQConnector $connector): void $queue->declareQueue( $this->argument('name'), - (bool)$this->option('durable'), - (bool)$this->option('auto-delete') + (bool) $this->option('durable'), + (bool) $this->option('auto-delete') ); } } diff --git a/src/Console/QueuePurgeCommand.php b/src/Console/QueuePurgeCommand.php index 235cac76..8b03604a 100644 --- a/src/Console/QueuePurgeCommand.php +++ b/src/Console/QueuePurgeCommand.php @@ -24,7 +24,7 @@ class QueuePurgeCommand extends Command */ public function handle(RabbitMQConnector $connector): void { - if (!$this->confirmToProceed()) { + if (! $this->confirmToProceed()) { return; } diff --git a/src/Horizon/RabbitMQQueue.php b/src/Horizon/RabbitMQQueue.php index a3577114..4389bd16 100644 --- a/src/Horizon/RabbitMQQueue.php +++ b/src/Horizon/RabbitMQQueue.php @@ -75,7 +75,7 @@ public function pop($queue = null) { return tap(parent::pop($queue), function ($result) use ($queue): void { if ($result instanceof RabbitMQJob) { - $this->event($queue ?: $this->default, new JobReserved($result->getRawBody())); + $this->event($this->getQueue($queue), new JobReserved($result->getRawBody())); } }); } diff --git a/src/Queue/Connectors/RabbitMQConnector.php b/src/Queue/Connectors/RabbitMQConnector.php index e2683aef..8492fb7c 100644 --- a/src/Queue/Connectors/RabbitMQConnector.php +++ b/src/Queue/Connectors/RabbitMQConnector.php @@ -46,7 +46,7 @@ public function connect(array $config): Queue $config['queue'] ); - if (!$queue instanceof RabbitMQQueue) { + if (! $queue instanceof RabbitMQQueue) { throw new InvalidArgumentException('Invalid worker.'); } diff --git a/src/Queue/Jobs/RabbitMQJob.php b/src/Queue/Jobs/RabbitMQJob.php index 9cc90937..fd09a0cb 100644 --- a/src/Queue/Jobs/RabbitMQJob.php +++ b/src/Queue/Jobs/RabbitMQJob.php @@ -8,8 +8,8 @@ use Illuminate\Support\Arr; use PhpAmqpLib\Message\AMQPMessage; use PhpAmqpLib\Wire\AMQPTable; -use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue; use VladimirYuldashev\LaravelQueueRabbitMQ\Horizon\RabbitMQQueue as HorizonRabbitMQQueue; +use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue; class RabbitMQJob extends Job implements JobContract { @@ -70,14 +70,14 @@ public function attempts(): int /** @var AMQPTable|null $headers */ $headers = Arr::get($this->message->get_properties(), 'application_headers'); - if (!$headers) { + if (! $headers) { return 0; } $data = $headers->getNativeData(); - $laravelAttempts = Arr::get($data, 'laravel.attempts', 0); - $xDeathCount = Arr::get($headers->getNativeData(), 'x-death.0.count', 0); + $laravelAttempts = (int) Arr::get($data, 'laravel.attempts', 0); + $xDeathCount = (int) Arr::get($headers->getNativeData(), 'x-death.0.count', 0); return $laravelAttempts + $xDeathCount; } diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index 40a45faf..8e29e6ed 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -78,7 +78,7 @@ public function size($queue = null): int { $queue = $this->getQueue($queue); - if (!$this->isQueueExists($queue)) { + if (! $this->isQueueExists($queue)) { return 0; } @@ -168,7 +168,7 @@ public function bulk($jobs, $data = '', $queue = null): void { $queue = $this->getQueue($queue); - foreach ((array)$jobs as $job) { + foreach ((array) $jobs as $job) { [$message] = $this->createMessage( $this->createPayload($job, $queue, $data) ); From 0439b1845cdac2e9499776b1b84813965fb8e5c0 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Tue, 26 Nov 2019 12:46:33 +0300 Subject: [PATCH 112/375] wip --- src/LaravelQueueRabbitMQServiceProvider.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/LaravelQueueRabbitMQServiceProvider.php b/src/LaravelQueueRabbitMQServiceProvider.php index 4e24a98e..c5c66b4b 100644 --- a/src/LaravelQueueRabbitMQServiceProvider.php +++ b/src/LaravelQueueRabbitMQServiceProvider.php @@ -4,7 +4,6 @@ use Illuminate\Queue\QueueManager; use Illuminate\Support\ServiceProvider; -use VladimirYuldashev\LaravelQueueRabbitMQ\Console; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Connectors\RabbitMQConnector; class LaravelQueueRabbitMQServiceProvider extends ServiceProvider From 12354f5fb7d9fd798410eebaf8633f13075c68a4 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Tue, 26 Nov 2019 12:47:13 +0300 Subject: [PATCH 113/375] wip --- src/Queue/Connectors/RabbitMQConnector.php | 1 - src/Queue/Jobs/RabbitMQJob.php | 1 - 2 files changed, 2 deletions(-) diff --git a/src/Queue/Connectors/RabbitMQConnector.php b/src/Queue/Connectors/RabbitMQConnector.php index 8492fb7c..79b25e0a 100644 --- a/src/Queue/Connectors/RabbitMQConnector.php +++ b/src/Queue/Connectors/RabbitMQConnector.php @@ -58,7 +58,6 @@ public function connect(array $config): Queue $queue->close(); }); - return $queue; } diff --git a/src/Queue/Jobs/RabbitMQJob.php b/src/Queue/Jobs/RabbitMQJob.php index fd09a0cb..d851d211 100644 --- a/src/Queue/Jobs/RabbitMQJob.php +++ b/src/Queue/Jobs/RabbitMQJob.php @@ -45,7 +45,6 @@ public function __construct( $this->decoded = $this->payload(); } - /** * {@inheritdoc} */ From 320bec20258ac8bf5939585a047d8c408ab2d497 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Tue, 26 Nov 2019 12:50:13 +0300 Subject: [PATCH 114/375] wip --- .styleci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.styleci.yml b/.styleci.yml index 0285f179..bdff1b9f 100644 --- a/.styleci.yml +++ b/.styleci.yml @@ -1 +1,4 @@ preset: laravel + +disabled: + - simplified_null_return From f751a48b80783fda2f48f284be8e492018178e75 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Sun, 1 Dec 2019 00:19:08 +0300 Subject: [PATCH 115/375] wip --- src/Console/ExchangeDeclareCommand.php | 2 +- src/Console/QueueDeclareCommand.php | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Console/ExchangeDeclareCommand.php b/src/Console/ExchangeDeclareCommand.php index 61c52db4..48cafaf1 100644 --- a/src/Console/ExchangeDeclareCommand.php +++ b/src/Console/ExchangeDeclareCommand.php @@ -40,6 +40,6 @@ public function handle(RabbitMQConnector $connector): void (bool) $this->option('auto-delete') ); - $this->warn('Exchange already exists.'); + $this->warn('Exchange declared successfully.'); } } diff --git a/src/Console/QueueDeclareCommand.php b/src/Console/QueueDeclareCommand.php index c3059668..0c89f329 100644 --- a/src/Console/QueueDeclareCommand.php +++ b/src/Console/QueueDeclareCommand.php @@ -37,5 +37,7 @@ public function handle(RabbitMQConnector $connector): void (bool) $this->option('durable'), (bool) $this->option('auto-delete') ); + + $this->info('Queue declared successfully.'); } } From 2152059992c425af9450655bab26b5bcdce385eb Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Sun, 1 Dec 2019 00:21:00 +0300 Subject: [PATCH 116/375] wip --- README.md | 52 ++++++++++++++++++++++++++-------------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index bde9b853..b6455923 100644 --- a/README.md +++ b/README.md @@ -35,34 +35,34 @@ Setup connection in `config/queue.php` 'rabbitmq' => [ - 'driver' => 'rabbitmq', - 'queue' => env('RABBITMQ_QUEUE', 'default'), - 'connection' => PhpAmqpLib\Connection\AMQPLazyConnection::class, - - 'hosts' => [ - [ - 'host' => env('RABBITMQ_HOST', '127.0.0.1'), - 'port' => env('RABBITMQ_PORT', 5672), - 'user' => env('RABBITMQ_USER', 'guest'), - 'password' => env('RABBITMQ_PASSWORD', 'guest'), - 'vhost' => env('RABBITMQ_VHOST', '/'), - ], - ], - - 'options' => [ - 'ssl_options' => [ - 'cafile' => env('RABBITMQ_SSL_CAFILE', null), - 'local_cert' => env('RABBITMQ_SSL_LOCALCERT', null), - 'local_key' => env('RABBITMQ_SSL_LOCALKEY', null), - 'verify_peer' => env('RABBITMQ_SSL_VERIFY_PEER', true), - 'passphrase' => env('RABBITMQ_SSL_PASSPHRASE', null), - ], - ], - - /* + 'driver' => 'rabbitmq', + 'queue' => env('RABBITMQ_QUEUE', 'default'), + 'connection' => PhpAmqpLib\Connection\AMQPLazyConnection::class, + + 'hosts' => [ + [ + 'host' => env('RABBITMQ_HOST', '127.0.0.1'), + 'port' => env('RABBITMQ_PORT', 5672), + 'user' => env('RABBITMQ_USER', 'guest'), + 'password' => env('RABBITMQ_PASSWORD', 'guest'), + 'vhost' => env('RABBITMQ_VHOST', '/'), + ], + ], + + 'options' => [ + 'ssl_options' => [ + 'cafile' => env('RABBITMQ_SSL_CAFILE', null), + 'local_cert' => env('RABBITMQ_SSL_LOCALCERT', null), + 'local_key' => env('RABBITMQ_SSL_LOCALKEY', null), + 'verify_peer' => env('RABBITMQ_SSL_VERIFY_PEER', true), + 'passphrase' => env('RABBITMQ_SSL_PASSPHRASE', null), + ], + ], + + /* * Set to "horizon" if you wish to use Laravel Horizon. */ - 'worker' => env('RABBITMQ_WORKER', 'default'), + 'worker' => env('RABBITMQ_WORKER', 'default'), ], From 803a416e1b0f2a3f5819641a42e1463cda13e201 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Sun, 1 Dec 2019 00:24:25 +0300 Subject: [PATCH 117/375] v10 (#284) v10 --- .gitattributes | 15 + .php_cs.dist | 4 + .styleci.yml | 3 + .travis.yml | 3 +- README.md | 164 ++----- composer.json | 6 +- config/rabbitmq.php | 91 +--- docker-compose.yml | 22 + src/Console/ExchangeDeclareCommand.php | 45 ++ src/Console/QueueBindCommand.php | 37 ++ src/Console/QueueDeclareCommand.php | 43 ++ src/Console/QueuePurgeCommand.php | 39 ++ src/Horizon/RabbitMQQueue.php | 52 ++- src/LaravelQueueRabbitMQServiceProvider.php | 9 + src/Queue/Connectors/RabbitMQConnector.php | 107 +++-- src/Queue/Jobs/RabbitMQJob.php | 184 +++----- src/Queue/RabbitMQQueue.php | 439 +++++++++++------- tests/Feature/ConnectorTest.php | 94 ++++ tests/Feature/QueueTest.php | 8 +- tests/Feature/SslQueueTest.php | 43 +- tests/Feature/TestCase.php | 234 +++++++++- tests/Mocks/AmqpConnectionFactorySpy.php | 25 - tests/Mocks/AmqpContextMock.php | 99 ---- ...CustomContextAmqpConnectionFactoryMock.php | 16 - ...yStrategyAwareAmqpConnectionFactorySpy.php | 34 -- tests/Mocks/TestJob.php | 10 +- tests/TestCase.php | 56 +-- .../Connectors/RabbitMQConnectorTest.php | 209 --------- tests/Unit/Queue/Jobs/RabbitMQJobTest.php | 86 ---- tests/Unit/Queue/RabbitMQQueueTest.php | 377 --------------- 30 files changed, 1085 insertions(+), 1469 deletions(-) create mode 100644 .gitattributes create mode 100644 src/Console/ExchangeDeclareCommand.php create mode 100644 src/Console/QueueBindCommand.php create mode 100644 src/Console/QueueDeclareCommand.php create mode 100644 src/Console/QueuePurgeCommand.php create mode 100644 tests/Feature/ConnectorTest.php delete mode 100644 tests/Mocks/AmqpConnectionFactorySpy.php delete mode 100644 tests/Mocks/AmqpContextMock.php delete mode 100644 tests/Mocks/CustomContextAmqpConnectionFactoryMock.php delete mode 100644 tests/Mocks/DelayStrategyAwareAmqpConnectionFactorySpy.php delete mode 100644 tests/Unit/Queue/Connectors/RabbitMQConnectorTest.php delete mode 100644 tests/Unit/Queue/Jobs/RabbitMQJobTest.php delete mode 100644 tests/Unit/Queue/RabbitMQQueueTest.php diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..9e5277b7 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,15 @@ +* text=auto + +/.github export-ignore +/tests export-ignore +.editorconfig export-ignore +.gitattributes export-ignore +.gitignore export-ignore +.styleci.yml export-ignore +.travis.yml export-ignore +.php_cs.dist export-ignore +CHANGELOG-* export-ignore +CODE_OF_CONDUCT.md export-ignore +CONTRIBUTING.md export-ignore +phpunit.xml.dist export-ignore +docker-compose.yml export-ignore diff --git a/.php_cs.dist b/.php_cs.dist index e8f48f80..930c161e 100644 --- a/.php_cs.dist +++ b/.php_cs.dist @@ -50,4 +50,8 @@ return PhpCsFixer\Config::create() ], 'fully_qualified_strict_types' => true, 'void_return' => true, + 'cast_spaces' => [ + 'space' => 'single', + ], + 'not_operator_with_successor_space' => true, ]); diff --git a/.styleci.yml b/.styleci.yml index 0285f179..bdff1b9f 100644 --- a/.styleci.yml +++ b/.styleci.yml @@ -1 +1,4 @@ preset: laravel + +disabled: + - simplified_null_return diff --git a/.travis.yml b/.travis.yml index d64690fa..960328da 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,6 @@ language: php php: - 7.2 - 7.3 - - 7.4snapshot services: - docker @@ -19,7 +18,7 @@ before_install: - sudo mv docker-compose /usr/local/bin before_script: - - docker-compose up -d + - docker-compose up -d rabbitmq - travis_retry composer self-update - travis_retry composer update --no-progress --no-interaction --prefer-dist - sleep 10 diff --git a/README.md b/README.md index cc980453..b6455923 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,17 @@ RabbitMQ Queue driver for Laravel [![StyleCI](https://styleci.io/repos/14976752/shield)](https://styleci.io/repos/14976752) [![License](https://poser.pugx.org/vladimir-yuldashev/laravel-queue-rabbitmq/license?format=flat-square)](https://packagist.org/packages/vladimir-yuldashev/laravel-queue-rabbitmq) +## Support Policy + +| Package Version | Laravel Version | Bug Fixes Until | +|-----------------|-----------------|---------------------| +| 6.0 | 5.5 | August 30th, 2019 | +| 7.0 | 5.6 | August 7th, 2018 | +| 7.1 | 5.7 | March 4th, 2019 | +| 7.2 | 5.8 | August 26th, 2019 | +| 9 | 6 | September 3rd, 2021 | +| 10 | 6 | September 3rd, 2021 | + ## Installation You can install this package via composer using this command: @@ -21,106 +32,40 @@ Setup connection in `config/queue.php` ```php 'connections' => [ // ... + 'rabbitmq' => [ - 'driver' => 'rabbitmq', - - /* - * Set to "horizon" if you wish to use Laravel Horizon. - */ - 'worker' => env('RABBITMQ_WORKER', 'default'), - - 'dsn' => env('RABBITMQ_DSN', null), - - /* - * Could be one a class that implements \Interop\Amqp\AmqpConnectionFactory for example: - * - \EnqueueAmqpExt\AmqpConnectionFactory if you install enqueue/amqp-ext - * - \EnqueueAmqpLib\AmqpConnectionFactory if you install enqueue/amqp-lib - * - \EnqueueAmqpBunny\AmqpConnectionFactory if you install enqueue/amqp-bunny - */ - - 'factory_class' => Enqueue\AmqpLib\AmqpConnectionFactory::class, - - 'host' => env('RABBITMQ_HOST', '127.0.0.1'), - 'port' => env('RABBITMQ_PORT', 5672), - - 'vhost' => env('RABBITMQ_VHOST', '/'), - 'login' => env('RABBITMQ_LOGIN', 'guest'), - 'password' => env('RABBITMQ_PASSWORD', 'guest'), - - 'queue' => env('RABBITMQ_QUEUE', 'default'), - - 'options' => [ - - 'exchange' => [ - - 'name' => env('RABBITMQ_EXCHANGE_NAME'), - - /* - * Determine if exchange should be created if it does not exist. - */ - - 'declare' => env('RABBITMQ_EXCHANGE_DECLARE', true), - - /* - * Read more about possible values at https://www.rabbitmq.com/tutorials/amqp-concepts.html - */ - - 'type' => env('RABBITMQ_EXCHANGE_TYPE', \Interop\Amqp\AmqpTopic::TYPE_DIRECT), - 'passive' => env('RABBITMQ_EXCHANGE_PASSIVE', false), - 'durable' => env('RABBITMQ_EXCHANGE_DURABLE', true), - 'auto_delete' => env('RABBITMQ_EXCHANGE_AUTODELETE', false), - 'arguments' => env('RABBITMQ_EXCHANGE_ARGUMENTS'), - ], - - 'queue' => [ - - /* - * Determine if queue should be created if it does not exist. - */ - - 'declare' => env('RABBITMQ_QUEUE_DECLARE', true), - - /* - * Determine if queue should be binded to the exchange created. - */ - - 'bind' => env('RABBITMQ_QUEUE_DECLARE_BIND', true), - - /* - * Read more about possible values at https://www.rabbitmq.com/tutorials/amqp-concepts.html - */ - - 'passive' => env('RABBITMQ_QUEUE_PASSIVE', false), - 'durable' => env('RABBITMQ_QUEUE_DURABLE', true), - 'exclusive' => env('RABBITMQ_QUEUE_EXCLUSIVE', false), - 'auto_delete' => env('RABBITMQ_QUEUE_AUTODELETE', false), - 'arguments' => env('RABBITMQ_QUEUE_ARGUMENTS'), - ], - ], - - /* - * Determine the number of seconds to sleep if there's an error communicating with rabbitmq - * If set to false, it'll throw an exception rather than doing the sleep for X seconds. - */ - - 'sleep_on_error' => env('RABBITMQ_ERROR_SLEEP', 5), - - /* - * Optional SSL params if an SSL connection is used - * Using an SSL connection will also require to configure your RabbitMQ to enable SSL. More details can be founds here: https://www.rabbitmq.com/ssl.html - */ - - 'ssl_params' => [ - 'ssl_on' => env('RABBITMQ_SSL', false), - 'cafile' => env('RABBITMQ_SSL_CAFILE', null), - 'local_cert' => env('RABBITMQ_SSL_LOCALCERT', null), - 'local_key' => env('RABBITMQ_SSL_LOCALKEY', null), - 'verify_peer' => env('RABBITMQ_SSL_VERIFY_PEER', true), - 'passphrase' => env('RABBITMQ_SSL_PASSPHRASE', null), - ], + 'driver' => 'rabbitmq', + 'queue' => env('RABBITMQ_QUEUE', 'default'), + 'connection' => PhpAmqpLib\Connection\AMQPLazyConnection::class, + + 'hosts' => [ + [ + 'host' => env('RABBITMQ_HOST', '127.0.0.1'), + 'port' => env('RABBITMQ_PORT', 5672), + 'user' => env('RABBITMQ_USER', 'guest'), + 'password' => env('RABBITMQ_PASSWORD', 'guest'), + 'vhost' => env('RABBITMQ_VHOST', '/'), + ], + ], + + 'options' => [ + 'ssl_options' => [ + 'cafile' => env('RABBITMQ_SSL_CAFILE', null), + 'local_cert' => env('RABBITMQ_SSL_LOCALCERT', null), + 'local_key' => env('RABBITMQ_SSL_LOCALKEY', null), + 'verify_peer' => env('RABBITMQ_SSL_VERIFY_PEER', true), + 'passphrase' => env('RABBITMQ_SSL_PASSPHRASE', null), + ], + ], + + /* + * Set to "horizon" if you wish to use Laravel Horizon. + */ + 'worker' => env('RABBITMQ_WORKER', 'default'), ], + // ... ], ``` @@ -141,37 +86,12 @@ For Lumen usage the service provider should be registered manually as follow in $app->register(VladimirYuldashev\LaravelQueueRabbitMQ\LaravelQueueRabbitMQServiceProvider::class); ``` - -## Using other AMQP transports - -The package uses [enqueue/amqp-lib](https://github.com/php-enqueue/enqueue-dev/blob/master/docs/transport/amqp_lib.md) transport which is based on [php-amqplib](https://github.com/php-amqplib/php-amqplib). -There is possibility to use any [amqp interop](https://github.com/queue-interop/queue-interop#amqp-interop) compatible transport, for example `enqueue/amqp-ext` or `enqueue/amqp-bunny`. -Here's an example on how one can change the transport to `enqueue/amqp-bunny`. - -First, install desired transport package: - -```bash -composer require enqueue/amqp-bunny:^0.8 -``` - -Change the factory class in `config/queue.php`: - -```php - // ... - 'connections' => [ - 'rabbitmq' => [ - 'driver' => 'rabbitmq', - 'factory_class' => Enqueue\AmqpBunny\AmqpConnectionFactory::class, - ], - ], -``` - ## Testing Setup RabbitMQ using `docker-compose`: ```bash -docker-compose up -d +docker-compose up -d rabbitmq ``` To run the test suite you can use the following commands: diff --git a/composer.json b/composer.json index 6094beb9..32f1a5e2 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "vladimir-yuldashev/laravel-queue-rabbitmq", - "description": "RabbitMQ driver for Laravel Queue. Supports Laravel Horizon", + "description": "RabbitMQ driver for Laravel Queue. Supports Laravel Horizon.", "license": "MIT", "authors": [ { @@ -14,15 +14,13 @@ "illuminate/database": "^6.0", "illuminate/support": "^6.0", "illuminate/queue": "^6.0", - "enqueue/amqp-lib": "0.9.*", - "queue-interop/amqp-interop": "0.8.*" + "php-amqplib/php-amqplib": "^2.11" }, "require-dev": { "phpunit/phpunit": "^8.4", "illuminate/events": "^6.0", "mockery/mockery": "^1.0", "laravel/horizon": "^3.0", - "larapack/dd": "^1.1", "friendsofphp/php-cs-fixer": "^2.16", "orchestra/testbench": "^4.3" }, diff --git a/config/rabbitmq.php b/config/rabbitmq.php index 02674904..765d73b8 100644 --- a/config/rabbitmq.php +++ b/config/rabbitmq.php @@ -8,85 +8,32 @@ return [ 'driver' => 'rabbitmq', - - /* - * Set to "horizon" if you wish to use Laravel Horizon. - */ - 'worker' => env('RABBITMQ_WORKER', 'default'), - - 'dsn' => env('RABBITMQ_DSN', null), - - /* - * Could be one a class that implements \Interop\Amqp\AmqpConnectionFactory for example: - * - \EnqueueAmqpExt\AmqpConnectionFactory if you install enqueue/amqp-ext - * - \EnqueueAmqpLib\AmqpConnectionFactory if you install enqueue/amqp-lib - * - \EnqueueAmqpBunny\AmqpConnectionFactory if you install enqueue/amqp-bunny - */ - 'factory_class' => Enqueue\AmqpLib\AmqpConnectionFactory::class, - - 'host' => env('RABBITMQ_HOST', '127.0.0.1'), - 'port' => env('RABBITMQ_PORT', 5672), - - 'vhost' => env('RABBITMQ_VHOST', '/'), - 'login' => env('RABBITMQ_LOGIN', 'guest'), - 'password' => env('RABBITMQ_PASSWORD', 'guest'), - 'queue' => env('RABBITMQ_QUEUE', 'default'), - - 'options' => [ - - 'exchange' => [ - - 'name' => env('RABBITMQ_EXCHANGE_NAME'), - - /* - * Determine if exchange should be created if it does not exist. - */ - 'declare' => env('RABBITMQ_EXCHANGE_DECLARE', true), - - /* - * Read more about possible values at https://www.rabbitmq.com/tutorials/amqp-concepts.html - */ - 'type' => env('RABBITMQ_EXCHANGE_TYPE', \Interop\Amqp\AmqpTopic::TYPE_DIRECT), - 'passive' => env('RABBITMQ_EXCHANGE_PASSIVE', false), - 'durable' => env('RABBITMQ_EXCHANGE_DURABLE', true), - 'auto_delete' => env('RABBITMQ_EXCHANGE_AUTODELETE', false), - 'arguments' => env('RABBITMQ_EXCHANGE_ARGUMENTS'), + 'connection' => PhpAmqpLib\Connection\AMQPLazyConnection::class, + + 'hosts' => [ + [ + 'host' => env('RABBITMQ_HOST', '127.0.0.1'), + 'port' => env('RABBITMQ_PORT', 5672), + 'user' => env('RABBITMQ_USER', 'guest'), + 'password' => env('RABBITMQ_PASSWORD', 'guest'), + 'vhost' => env('RABBITMQ_VHOST', '/'), ], + ], - 'queue' => [ - - /* - * Determine if queue should be created if it does not exist. - */ - 'declare' => env('RABBITMQ_QUEUE_DECLARE', true), - - /* - * Determine if queue should be binded to the exchange created. - */ - 'bind' => env('RABBITMQ_QUEUE_DECLARE_BIND', true), - - /* - * Read more about possible values at https://www.rabbitmq.com/tutorials/amqp-concepts.html - */ - 'passive' => env('RABBITMQ_QUEUE_PASSIVE', false), - 'durable' => env('RABBITMQ_QUEUE_DURABLE', true), - 'exclusive' => env('RABBITMQ_QUEUE_EXCLUSIVE', false), - 'auto_delete' => env('RABBITMQ_QUEUE_AUTODELETE', false), - 'arguments' => env('RABBITMQ_QUEUE_ARGUMENTS'), + 'options' => [ + 'ssl_options' => [ + 'cafile' => env('RABBITMQ_SSL_CAFILE', null), + 'local_cert' => env('RABBITMQ_SSL_LOCALCERT', null), + 'local_key' => env('RABBITMQ_SSL_LOCALKEY', null), + 'verify_peer' => env('RABBITMQ_SSL_VERIFY_PEER', true), + 'passphrase' => env('RABBITMQ_SSL_PASSPHRASE', null), ], ], /* - * Optional SSL params if an SSL connection is used + * Set to "horizon" if you wish to use Laravel Horizon. */ - 'ssl_params' => [ - 'ssl_on' => env('RABBITMQ_SSL', false), - 'cafile' => env('RABBITMQ_SSL_CAFILE', null), - 'local_cert' => env('RABBITMQ_SSL_LOCALCERT', null), - 'local_key' => env('RABBITMQ_SSL_LOCALKEY', null), - 'verify_peer' => env('RABBITMQ_SSL_VERIFY_PEER', true), - 'passphrase' => env('RABBITMQ_SSL_PASSPHRASE', null), - ], + 'worker' => env('RABBITMQ_WORKER', 'default'), ]; diff --git a/docker-compose.yml b/docker-compose.yml index 189e4ac8..6052f7f9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -17,5 +17,27 @@ services: - "./tests/files/rootCA.pem:/rootCA.pem:ro" - "./tests/files/rootCA.key:/rootCA.key:ro" ports: + - 15671:15671 + - 15672:15672 + - 5671:5671 + - 5672:5672 + + rabbitmq-management: + image: rabbitmq:management + environment: + RABBITMQ_DEFAULT_USER: guest + RABBITMQ_DEFAULT_PASSWORD: guest + RABBITMQ_DEFAULT_VHOST: / + RABBITMQ_MANAGEMENT_SSL_CACERTFILE: /rootCA.pem + RABBITMQ_MANAGEMENT_SSL_CERTFILE: /rootCA.pem + RABBITMQ_MANAGEMENT_SSL_KEYFILE: /rootCA.key + RABBITMQ_MANAGEMENT_SSL_VERIFY: verify_none + RABBITMQ_MANAGEMENT_SSL_FAIL_IF_NO_PEER_CERT: "false" + volumes: + - "./tests/files/rootCA.pem:/rootCA.pem:ro" + - "./tests/files/rootCA.key:/rootCA.key:ro" + ports: + - 15671:15671 + - 15672:15672 - 5671:5671 - 5672:5672 diff --git a/src/Console/ExchangeDeclareCommand.php b/src/Console/ExchangeDeclareCommand.php new file mode 100644 index 00000000..48cafaf1 --- /dev/null +++ b/src/Console/ExchangeDeclareCommand.php @@ -0,0 +1,45 @@ +laravel['config']->get('queue.connections.'.$this->argument('connection')); + + $queue = $connector->connect($config); + + if ($queue->isExchangeExists($this->argument('name'))) { + $this->warn('Exchange already exists.'); + + return; + } + + $queue->declareExchange( + $this->argument('name'), + $this->argument('type'), + (bool) $this->option('durable'), + (bool) $this->option('auto-delete') + ); + + $this->warn('Exchange declared successfully.'); + } +} diff --git a/src/Console/QueueBindCommand.php b/src/Console/QueueBindCommand.php new file mode 100644 index 00000000..47c4ea3a --- /dev/null +++ b/src/Console/QueueBindCommand.php @@ -0,0 +1,37 @@ +laravel['config']->get('queue.connections.'.$this->argument('connection')); + + $queue = $connector->connect($config); + + $queue->bindQueue( + $this->argument('queue'), + $this->argument('exchange'), + (string) $this->option('routing-key') + ); + + $this->info('Queue bound to exchange successfully.'); + } +} diff --git a/src/Console/QueueDeclareCommand.php b/src/Console/QueueDeclareCommand.php new file mode 100644 index 00000000..0c89f329 --- /dev/null +++ b/src/Console/QueueDeclareCommand.php @@ -0,0 +1,43 @@ +laravel['config']->get('queue.connections.'.$this->argument('connection')); + + $queue = $connector->connect($config); + + if ($queue->isQueueExists($this->argument('name'))) { + $this->warn('Queue already exists.'); + + return; + } + + $queue->declareQueue( + $this->argument('name'), + (bool) $this->option('durable'), + (bool) $this->option('auto-delete') + ); + + $this->info('Queue declared successfully.'); + } +} diff --git a/src/Console/QueuePurgeCommand.php b/src/Console/QueuePurgeCommand.php new file mode 100644 index 00000000..8b03604a --- /dev/null +++ b/src/Console/QueuePurgeCommand.php @@ -0,0 +1,39 @@ +confirmToProceed()) { + return; + } + + $config = $this->laravel['config']->get('queue.connections.'.$this->argument('connection')); + + $queue = $connector->connect($config); + + $queue->purge($this->argument('queue')); + + $this->info('Queue purged successfully.'); + } +} diff --git a/src/Horizon/RabbitMQQueue.php b/src/Horizon/RabbitMQQueue.php index 04f377e2..4389bd16 100644 --- a/src/Horizon/RabbitMQQueue.php +++ b/src/Horizon/RabbitMQQueue.php @@ -9,6 +9,7 @@ use Laravel\Horizon\Events\JobReserved; use Laravel\Horizon\JobId; use Laravel\Horizon\JobPayload; +use PhpAmqpLib\Exception\AMQPProtocolChannelException; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Jobs\RabbitMQJob; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue as BaseRabbitMQQueue; @@ -24,15 +25,18 @@ class RabbitMQQueue extends BaseRabbitMQQueue /** * Get the number of queue jobs that are ready to process. * - * @param string|null $queue + * @param string|null $queue * @return int + * @throws AMQPProtocolChannelException */ public function readyNow($queue = null): int { return $this->size($queue); } - /** {@inheritdoc} */ + /** + * {@inheritdoc} + */ public function push($job, $data = '', $queue = null) { $this->lastPushed = $job; @@ -40,37 +44,45 @@ public function push($job, $data = '', $queue = null) return parent::push($job, $data, $queue); } - /** {@inheritdoc} */ - public function pushRaw($payload, $queueName = null, array $options = []) + /** + * {@inheritdoc} + */ + public function pushRaw($payload, $queue = null, array $options = []) { $payload = (new JobPayload($payload))->prepare($this->lastPushed)->value; - return tap(parent::pushRaw($payload, $queueName, $options), function () use ($queueName, $payload): void { - $this->event($this->getQueueName($queueName), new JobPushed($payload)); + return tap(parent::pushRaw($payload, $queue, $options), function () use ($queue, $payload): void { + $this->event($this->getQueue($queue), new JobPushed($payload)); }); } - /** {@inheritdoc} */ - public function later($delay, $job, $data = '', $queueName = null) + /** + * {@inheritdoc} + */ + public function later($delay, $job, $data = '', $queue = null) { $payload = (new JobPayload($this->createPayload($job, $data)))->prepare($job)->value; - return tap(parent::pushRaw($payload, $queueName, ['delay' => $this->secondsUntil($delay)]), function () use ($payload, $queueName): void { - $this->event($this->getQueueName($queueName), new JobPushed($payload)); + return tap(parent::pushRaw($payload, $queue, ['delay' => $this->secondsUntil($delay)]), function () use ($payload, $queue): void { + $this->event($this->getQueue($queue), new JobPushed($payload)); }); } - /** {@inheritdoc} */ - public function pop($queueName = null) + /** + * {@inheritdoc} + */ + public function pop($queue = null) { - return tap(parent::pop($queueName), function ($result) use ($queueName): void { + return tap(parent::pop($queue), function ($result) use ($queue): void { if ($result instanceof RabbitMQJob) { - $this->event($queueName ?: $this->queueName, new JobReserved($result->getRawBody())); + $this->event($this->getQueue($queue), new JobReserved($result->getRawBody())); } }); } - /** {@inheritdoc} */ + /** + * {@inheritdoc} + */ public function release($delay, $job, $data, $queue, $attempts = 0) { $this->lastPushed = $job; @@ -81,14 +93,14 @@ public function release($delay, $job, $data, $queue, $attempts = 0) /** * Fire the job deleted event. * - * @param string $queueName + * @param string $queue * @param RabbitMQJob $job * @return void * @throws BindingResolutionException */ - public function deleteReserved($queueName, $job): void + public function deleteReserved($queue, $job): void { - $this->event($this->getQueueName($queueName), new JobDeleted($job, $job->getRawBody())); + $this->event($this->getQueue($queue), new JobDeleted($job, $job->getRawBody())); } /** @@ -108,7 +120,9 @@ protected function event($queue, $event): void } } - /** {@inheritdoc} */ + /** + * {@inheritdoc} + */ protected function getRandomId(): string { return JobId::generate(); diff --git a/src/LaravelQueueRabbitMQServiceProvider.php b/src/LaravelQueueRabbitMQServiceProvider.php index ac210ee9..c5c66b4b 100644 --- a/src/LaravelQueueRabbitMQServiceProvider.php +++ b/src/LaravelQueueRabbitMQServiceProvider.php @@ -19,6 +19,15 @@ public function register(): void __DIR__.'/../config/rabbitmq.php', 'queue.connections.rabbitmq' ); + + if ($this->app->runningInConsole()) { + $this->commands([ + Console\ExchangeDeclareCommand::class, + Console\QueueBindCommand::class, + Console\QueueDeclareCommand::class, + Console\QueuePurgeCommand::class, + ]); + } } /** diff --git a/src/Queue/Connectors/RabbitMQConnector.php b/src/Queue/Connectors/RabbitMQConnector.php index 1998b6f2..79b25e0a 100644 --- a/src/Queue/Connectors/RabbitMQConnector.php +++ b/src/Queue/Connectors/RabbitMQConnector.php @@ -2,22 +2,16 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Connectors; -use Enqueue\AmqpLib\AmqpConnectionFactory as EnqueueAmqpConnectionFactory; -use Enqueue\AmqpTools\DelayStrategyAware; -use Enqueue\AmqpTools\RabbitMqDlxDelayStrategy; +use Exception; use Illuminate\Contracts\Events\Dispatcher; use Illuminate\Contracts\Queue\Queue; use Illuminate\Queue\Connectors\ConnectorInterface; use Illuminate\Queue\Events\JobFailed; use Illuminate\Queue\Events\WorkerStopping; use Illuminate\Support\Arr; -use Interop\Amqp\AmqpConnectionFactory; -use Interop\Amqp\AmqpConnectionFactory as InteropAmqpConnectionFactory; -use Interop\Amqp\AmqpContext; use InvalidArgumentException; -use LogicException; -use ReflectionClass; -use ReflectionException; +use PhpAmqpLib\Connection\AbstractConnection; +use PhpAmqpLib\Connection\AMQPLazyConnection; use VladimirYuldashev\LaravelQueueRabbitMQ\Horizon\Listeners\RabbitMQFailedEvent; use VladimirYuldashev\LaravelQueueRabbitMQ\Horizon\RabbitMQQueue as HorizonRabbitMQQueue; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue; @@ -39,72 +33,77 @@ public function __construct(Dispatcher $dispatcher) * * @param array $config * - * @return Queue - * @throws ReflectionException + * @return RabbitMQQueue + * @throws Exception */ public function connect(array $config): Queue { - /** @var AmqpContext $context */ - $context = self::createContext($config); + $connection = $this->createConnection($config); - $this->dispatcher->listen(WorkerStopping::class, function () use ($context): void { - $context->close(); - }); - - $worker = Arr::get($config, 'worker', 'default'); + $queue = $this->createQueue( + Arr::get($config, 'worker', 'default'), + $connection, + $config['queue'] + ); - if ($worker === 'default') { - return new RabbitMQQueue($context, $config); + if (! $queue instanceof RabbitMQQueue) { + throw new InvalidArgumentException('Invalid worker.'); } - if ($worker === 'horizon') { + if ($queue instanceof HorizonRabbitMQQueue) { $this->dispatcher->listen(JobFailed::class, RabbitMQFailedEvent::class); - - return new HorizonRabbitMQQueue($context, $config); } - if ($worker instanceof RabbitMQQueue) { - return new $worker($context, $config); - } + $this->dispatcher->listen(WorkerStopping::class, static function () use ($queue): void { + $queue->close(); + }); - throw new InvalidArgumentException('Invalid worker.'); + return $queue; } /** - * Create a context. - * * @param array $config - * @return AmqpContext - * @throws ReflectionException + * @return AbstractConnection + * @throws Exception */ - public static function createContext(array $config): AmqpContext + protected function createConnection(array $config): AbstractConnection { - $factoryClass = Arr::get($config, 'factory_class', EnqueueAmqpConnectionFactory::class); + /** @var AbstractConnection $connection */ + $connection = Arr::get($config, 'connection', AMQPLazyConnection::class); - if (! class_exists($factoryClass) || ! (new ReflectionClass($factoryClass))->implementsInterface(InteropAmqpConnectionFactory::class)) { - throw new LogicException(sprintf('The factory_class option has to be valid class that implements "%s"', InteropAmqpConnectionFactory::class)); + return $connection::create_connection( + Arr::get($config, 'hosts', []), + $this->filter(Arr::get($config, 'options', [])) + ); + } + + protected function createQueue(string $worker, AbstractConnection $connection, string $queue) + { + switch ($worker) { + case 'default': + return new RabbitMQQueue($connection, $queue); + case 'horizon': + return new HorizonRabbitMQQueue($connection, $queue); + default: + return new $worker($connection, $queue); } + } - /** @var AmqpConnectionFactory $factory */ - $factory = new $factoryClass([ - 'dsn' => Arr::get($config, 'dsn'), - 'host' => Arr::get($config, 'host', '127.0.0.1'), - 'port' => Arr::get($config, 'port', 5672), - 'user' => Arr::get($config, 'login', 'guest'), - 'pass' => Arr::get($config, 'password', 'guest'), - 'vhost' => Arr::get($config, 'vhost', '/'), - 'ssl_on' => Arr::get($config, 'ssl_params.ssl_on', false), - 'ssl_verify' => Arr::get($config, 'ssl_params.verify_peer', true), - 'ssl_cacert' => Arr::get($config, 'ssl_params.cafile'), - 'ssl_cert' => Arr::get($config, 'ssl_params.local_cert'), - 'ssl_key' => Arr::get($config, 'ssl_params.local_key'), - 'ssl_passphrase' => Arr::get($config, 'ssl_params.passphrase'), - ]); - - if ($factory instanceof DelayStrategyAware) { - $factory->setDelayStrategy(new RabbitMqDlxDelayStrategy()); + private function filter(array $array): array + { + foreach ($array as $index => &$value) { + if (is_array($value)) { + $value = $this->filter($value); + continue; + } + + // If the value is null then remove it. + if ($value === null) { + unset($array[$index]); + continue; + } } - return $factory->createContext(); + return $array; } } diff --git a/src/Queue/Jobs/RabbitMQJob.php b/src/Queue/Jobs/RabbitMQJob.php index 0312a921..d851d211 100644 --- a/src/Queue/Jobs/RabbitMQJob.php +++ b/src/Queue/Jobs/RabbitMQJob.php @@ -2,183 +2,137 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Jobs; -use Exception; -use Illuminate\Container\Container; +use Illuminate\Contracts\Container\BindingResolutionException; use Illuminate\Contracts\Queue\Job as JobContract; -use Illuminate\Database\DetectsLostConnections; use Illuminate\Queue\Jobs\Job; -use Illuminate\Queue\Jobs\JobName; -use Illuminate\Support\Str; -use Interop\Amqp\AmqpConsumer; -use Interop\Amqp\AmqpMessage; +use Illuminate\Support\Arr; +use PhpAmqpLib\Message\AMQPMessage; +use PhpAmqpLib\Wire\AMQPTable; use VladimirYuldashev\LaravelQueueRabbitMQ\Horizon\RabbitMQQueue as HorizonRabbitMQQueue; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue; class RabbitMQJob extends Job implements JobContract { - use DetectsLostConnections; - /** - * Same as RabbitMQQueue, used for attempt counts. + * The RabbitMQ queue instance. + * + * @var RabbitMQQueue */ - public const ATTEMPT_COUNT_HEADERS_KEY = 'attempts_count'; + protected $rabbitmq; - protected $connection; - protected $consumer; + /** + * The RabbitMQ message instance. + * + * @var AMQPMessage + */ protected $message; + /** + * The JSON decoded version of "$message". + * + * @var array + */ + protected $decoded; + public function __construct( - Container $container, - RabbitMQQueue $connection, - AmqpConsumer $consumer, - AmqpMessage $message + RabbitMQQueue $rabbitmq, + AMQPMessage $message, + string $queue ) { - $this->container = $container; - $this->connection = $connection; - $this->consumer = $consumer; + $this->rabbitmq = $rabbitmq; $this->message = $message; - $this->queue = $consumer->getQueue()->getQueueName(); - $this->connectionName = $connection->getConnectionName(); + $this->queue = $queue; + $this->decoded = $this->payload(); } /** - * Fire the job. - * - * @throws Exception - * - * @return void + * {@inheritdoc} */ - public function fire(): void + public function getJobId() { - try { - $payload = $this->payload(); - - [$class, $method] = JobName::parse($payload['job']); - - with($this->instance = $this->resolve($class))->{$method}($this, $payload['data']); - } catch (Exception $exception) { - if ( - $this->causedByLostConnection($exception) || - Str::contains($exception->getMessage(), ['detected deadlock']) - ) { - sleep(2); - $this->fire(); - - return; - } + return json_decode($this->message->getBody(), true)['id'] ?? null; + } - throw $exception; - } + /** + * {@inheritdoc} + */ + public function getRawBody(): string + { + return $this->message->getBody(); } /** - * Get the number of times the job has been attempted. - * - * @return int + * {@inheritdoc} */ public function attempts(): int { - // set default job attempts to 1 so that jobs can run without retry - $defaultAttempts = 1; + /** @var AMQPTable|null $headers */ + $headers = Arr::get($this->message->get_properties(), 'application_headers'); - return $this->message->getProperty(self::ATTEMPT_COUNT_HEADERS_KEY, $defaultAttempts); + if (! $headers) { + return 0; + } + + $data = $headers->getNativeData(); + + $laravelAttempts = (int) Arr::get($data, 'laravel.attempts', 0); + $xDeathCount = (int) Arr::get($headers->getNativeData(), 'x-death.0.count', 0); + + return $laravelAttempts + $xDeathCount; } /** - * Get the raw body string for the job. + * {@inheritdoc} * - * @return string + * @throws BindingResolutionException */ - public function getRawBody(): string - { - return $this->message->getBody(); - } - - /** {@inheritdoc} */ public function delete(): void { parent::delete(); - $this->consumer->acknowledge($this->message); + $this->rabbitmq->ack($this); // required for Laravel Horizon - if ($this->connection instanceof HorizonRabbitMQQueue) { - $this->connection->deleteReserved($this->queue, $this); + if ($this->rabbitmq instanceof HorizonRabbitMQQueue) { + $this->rabbitmq->deleteReserved($this->queue, $this); } } - /** {@inheritdoc} - * @throws Exception + /** + * {@inheritdoc} */ public function release($delay = 0): void { parent::release($delay); - $this->delete(); + if ($delay > 0) { + $this->rabbitmq->ack($this); - $body = $this->payload(); + $this->rabbitmq->laterRaw($delay, $this->message->body, $this->queue, $this->attempts()); - /* - * Some jobs don't have the command set, so fall back to just sending it the job name string - */ - if (isset($body['data']['command']) === true) { - $job = $this->unserialize($body); - } else { - $job = $this->getName(); + return; } - $data = $body['data']; - - $this->connection->release($delay, $job, $data, $this->getQueue(), $this->attempts() + 1); + $this->rabbitmq->reject($this); } /** - * Get the job identifier. + * Get the underlying RabbitMQ connection. * - * @return string|null + * @return RabbitMQQueue */ - public function getJobId(): ?string + public function getRabbitMQ(): RabbitMQQueue { - return $this->message->getCorrelationId(); + return $this->rabbitmq; } /** - * Sets the job identifier. + * Get the underlying RabbitMQ message. * - * @param string $id - * - * @return void - */ - public function setJobId($id): void - { - $this->connection->setCorrelationId($id); - } - - /** - * Unserialize job. - * - * @param array $body - * - * @throws Exception - * - * @return mixed + * @return AMQPMessage */ - protected function unserialize(array $body) + public function getRabbitMQMessage(): AMQPMessage { - try { - /* @noinspection UnserializeExploitsInspection */ - return unserialize($body['data']['command']); - } catch (Exception $exception) { - if ( - $this->causedByLostConnection($exception) || - Str::contains($exception->getMessage(), ['detected deadlock']) - ) { - sleep(2); - - return $this->unserialize($body); - } - - throw $exception; - } + return $this->message; } } diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index 5699d102..8e29e6ed 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -1,66 +1,97 @@ config = $config; - $this->context = $context; + /** + * List of already declared exchanges. + * + * @var array + */ + protected $exchanges = []; - $this->queueName = $config['queue'] ?? $config['options']['queue']['name']; - $this->queueOptions = $config['options']['queue']; - $this->queueOptions['arguments'] = isset($this->queueOptions['arguments']) ? - json_decode($this->queueOptions['arguments'], true) : []; + /** + * List of already declared queues. + * + * @var array + */ + protected $queues = []; - $this->exchangeOptions = $config['options']['exchange']; - $this->exchangeOptions['arguments'] = isset($this->exchangeOptions['arguments']) ? - json_decode($this->exchangeOptions['arguments'], true) : []; + /** + * List of already bound queues to exchanges. + * + * @var array + */ + protected $boundQueues = []; + + public function __construct( + AbstractConnection $connection, + string $default + ) { + $this->connection = $connection; + $this->channel = $connection->channel(); + $this->default = $default; } /** * {@inheritdoc} + * + * @throws AMQPProtocolChannelException */ - public function size($queueName = null): int + public function size($queue = null): int { - /** @var AmqpQueue $queue */ - [$queue] = $this->declareEverything($queueName); + $queue = $this->getQueue($queue); + + if (! $this->isQueueExists($queue)) { + return 0; + } - return $this->context->declareQueue($queue); + // create a temporary channel, so the main channel will not be closed on exception + $channel = $this->connection->channel(); + [, $size] = $channel->queue_declare($queue, true); + $channel->close(); + + return $size; } /** * {@inheritdoc} - * - * @throws Exception */ public function push($job, $data = '', $queue = null) { @@ -69,188 +100,282 @@ public function push($job, $data = '', $queue = null) /** * {@inheritdoc} - * - * @throws Exception */ - public function pushRaw($payload, $queueName = null, array $options = []) + public function pushRaw($payload, $queue = null, array $options = []) { - /** - * @var AmqpTopic - * @var AmqpQueue $queue - */ - [$queue, $topic] = $this->declareEverything($queueName); - - /** @var AmqpMessage $message */ - $message = $this->context->createMessage($payload); - - $message->setCorrelationId($this->getCorrelationId()); - $message->setContentType('application/json'); - $message->setDeliveryMode(AmqpMessage::DELIVERY_MODE_PERSISTENT); - - if (isset($options['routing_key'])) { - $message->setRoutingKey($options['routing_key']); - } else { - $message->setRoutingKey($queue->getQueueName()); - } + $queue = $this->getQueue($queue); - if (isset($options['priority'])) { - $message->setPriority($options['priority']); - } - - if (isset($options['expiration'])) { - $message->setExpiration($options['expiration']); - } - - if (isset($options['headers'])) { - $message->setHeaders($options['headers']); - } - - if (isset($options['properties'])) { - $message->setProperties($options['properties']); - } - - if (isset($options['attempts'])) { - $message->setProperty(RabbitMQJob::ATTEMPT_COUNT_HEADERS_KEY, $options['attempts']); - } + $this->declareExchange($queue); + $this->declareQueue($queue, true, false, [ + 'x-dead-letter-exchange' => $queue, + 'x-dead-letter-routing-key' => $queue, + ]); + $this->bindQueue($queue, $queue, $queue); - $producer = $this->context->createProducer(); - if (isset($options['delay']) && $options['delay'] > 0) { - $producer->setDeliveryDelay($options['delay'] * 1000); - } + [$message, $correlationId] = $this->createMessage($payload); - $producer->send($topic, $message); + $this->channel->basic_publish($message, $queue, $queue, true, false); - return $message->getCorrelationId(); + return $correlationId; } /** * {@inheritdoc} - * @throws Exception */ public function later($delay, $job, $data = '', $queue = null) { - return $this->pushRaw($this->createPayload($job, $queue, $data), $queue, ['delay' => $this->secondsUntil($delay)]); + return $this->laterRaw( + $delay, + $this->createPayload($job, $queue, $data), + $queue + ); } - /** - * {@inheritDoc} - * @throws Exception - */ - public function release($delay, $job, $data, $queue, $attempts = 0) + public function laterRaw($delay, $payload, $queue = null, $attempts = 0) { - return $this->pushRaw($this->createPayload($job, $queue, $data), $queue, [ - 'delay' => $this->secondsUntil($delay), - 'attempts' => $attempts, + $ttl = $this->secondsUntil($delay) * 1000; + + if ($ttl < 0) { + return $this->pushRaw($payload, $queue, []); + } + + $destinationQueue = $this->getQueue($queue); + $delayedQueue = $this->getQueue($queue).'.delay.'.$ttl; + + $this->declareExchange($destinationQueue); + $this->declareQueue($destinationQueue, true, false, [ + 'x-dead-letter-exchange' => $destinationQueue, + 'x-dead-letter-routing-key' => $destinationQueue, + ]); + $this->declareQueue($delayedQueue, true, false, [ + 'x-dead-letter-exchange' => $destinationQueue, + 'x-dead-letter-routing-key' => $destinationQueue, + 'x-message-ttl' => $ttl, ]); + $this->bindQueue($destinationQueue, $destinationQueue, $destinationQueue); + + [$message, $correlationId] = $this->createMessage($payload, $attempts); + + $this->channel->basic_publish($message, null, $delayedQueue, true, false); + + return $correlationId; } /** * {@inheritdoc} */ - public function pop($queueName = null) + public function bulk($jobs, $data = '', $queue = null): void { - /** @var AmqpQueue $queue */ - [$queue] = $this->declareEverything($queueName); + $queue = $this->getQueue($queue); - $consumer = $this->context->createConsumer($queue); + foreach ((array) $jobs as $job) { + [$message] = $this->createMessage( + $this->createPayload($job, $queue, $data) + ); - if ($message = $consumer->receiveNoWait()) { - return new RabbitMQJob($this->container, $this, $consumer, $message); + $this->declareExchange($queue); + $this->declareQueue($queue, true, false, [ + 'x-dead-letter-exchange' => $queue, + 'x-dead-letter-routing-key' => $queue, + ]); + $this->bindQueue($queue, $queue, $queue); + + $this->channel->batch_basic_publish($message, $queue, $queue); } - return null; + $this->channel->publish_batch(); } /** - * Retrieves the correlation id, or a unique id. + * {@inheritdoc} * - * @return string + * @throws Exception */ - public function getCorrelationId(): string + public function pop($queue = null) { - return $this->correlationId ?: uniqid('', true); + try { + $queue = $this->getQueue($queue); + + /** @var AMQPMessage|null $message */ + if ($message = $this->channel->basic_get($queue)) { + return new RabbitMQJob($this, $message, $queue); + } + } catch (AMQPProtocolChannelException $exception) { + // if there is not exchange or queue AMQP will throw exception with code 404 + // we need to catch it and return null + if ($exception->amqp_reply_code === 404) { + return null; + } + + throw $exception; + } + + return null; } - /** - * Sets the correlation id for a message to be published. - * - * @param string $id - * - * @return void - */ - public function setCorrelationId(string $id): void + public function getConnection(): AbstractConnection { - $this->correlationId = $id; + return $this->connection; } - /** - * @return AmqpContext - */ - public function getContext(): AmqpContext + public function getChannel(): AMQPChannel + { + return $this->channel; + } + + public function getQueue($queue = null) { - return $this->context; + return $queue ?: $this->default; } /** - * @param string $queueName - * - * @return array [Interop\Amqp\AmqpQueue, Interop\Amqp\AmqpTopic] + * @param string $exchange + * @return bool + * @throws AMQPProtocolChannelException */ - public function declareEverything(string $queueName = null): array + public function isExchangeExists(string $exchange): bool { - $queueName = $this->getQueueName($queueName); - $exchangeName = $this->exchangeOptions['name'] ?: $queueName; - - $topic = $this->context->createTopic($exchangeName); - $topic->setType($this->exchangeOptions['type']); - $topic->setArguments($this->exchangeOptions['arguments']); - if ($this->exchangeOptions['passive']) { - $topic->addFlag(AmqpTopic::FLAG_PASSIVE); + try { + // create a temporary channel, so the main channel will not be closed on exception + $channel = $this->connection->channel(); + $channel->exchange_declare($exchange, '', true); + $channel->close(); + + return true; + } catch (AMQPProtocolChannelException $exception) { + if ($exception->amqp_reply_code === 404) { + return false; + } + + throw $exception; } - if ($this->exchangeOptions['durable']) { - $topic->addFlag(AmqpTopic::FLAG_DURABLE); - } - if ($this->exchangeOptions['auto_delete']) { - $topic->addFlag(AmqpTopic::FLAG_AUTODELETE); + } + + public function declareExchange( + string $name, + string $type = AMQPExchangeType::DIRECT, + bool $durable = true, + bool $autoDelete = false + ): void { + if (in_array($name, $this->exchanges, true)) { + return; } - if ($this->exchangeOptions['declare'] && !in_array($exchangeName, $this->declaredExchanges, true)) { - $this->context->declareTopic($topic); + $this->channel->exchange_declare( + $name, + $type, + false, + $durable, + $autoDelete, + false, + true + ); + } - $this->declaredExchanges[] = $exchangeName; - } + /** + * @param string $name + * @return bool + * @throws AMQPProtocolChannelException + */ + public function isQueueExists(?string $name = null): bool + { + try { + $name = $this->getQueue($name); - $queue = $this->context->createQueue($queueName); - $queue->setArguments($this->queueOptions['arguments']); - if ($this->queueOptions['passive']) { - $queue->addFlag(AmqpQueue::FLAG_PASSIVE); - } - if ($this->queueOptions['durable']) { - $queue->addFlag(AmqpQueue::FLAG_DURABLE); - } - if ($this->queueOptions['exclusive']) { - $queue->addFlag(AmqpQueue::FLAG_EXCLUSIVE); + // create a temporary channel, so the main channel will not be closed on exception + $channel = $this->connection->channel(); + $channel->queue_declare($name, true); + $channel->close(); + + return true; + } catch (AMQPProtocolChannelException $exception) { + if ($exception->amqp_reply_code === 404) { + return false; + } + + throw $exception; } - if ($this->queueOptions['auto_delete']) { - $queue->addFlag(AmqpQueue::FLAG_AUTODELETE); + } + + public function declareQueue(string $name, bool $durable = true, bool $autoDelete = false, array $arguments = []): void + { + if (in_array($name, $this->queues, true)) { + return; } - if ($this->queueOptions['declare'] && !in_array($queueName, $this->declaredQueues, true)) { - $this->context->declareQueue($queue); + $this->channel->queue_declare( + $name, + false, + $durable, + false, + $autoDelete, + false, + new AMQPTable($arguments) + ); + } - $this->declaredQueues[] = $queueName; + public function bindQueue(string $queue, string $exchange, string $routingKey = ''): void + { + if (in_array( + implode('', compact('queue', 'exchange', 'routingKey')), + $this->boundQueues, + true + )) { + return; } - if ($this->queueOptions['bind']) { - $this->context->bind(new AmqpBind($queue, $topic, $queue->getQueueName())); - } + $this->channel->queue_bind($queue, $exchange, $routingKey); + } + + public function purge($queue = null): void + { + // create a temporary channel, so the main channel will not be closed on exception + $channel = $this->connection->channel(); + $channel->queue_purge($this->getQueue($queue)); + $channel->close(); + } + + public function ack(RabbitMQJob $job): void + { + $this->channel->basic_ack($job->getRabbitMQMessage()->getDeliveryTag()); + } - return [$queue, $topic]; + public function reject(RabbitMQJob $job, bool $requeue = false): void + { + $this->channel->basic_reject($job->getRabbitMQMessage()->getDeliveryTag(), $requeue); } - protected function getQueueName($queueName = null) + /** + * @throws Exception + */ + public function close(): void { - return $queueName ?: $this->queueName; + $this->connection->close(); + } + + protected function createMessage($payload, int $attempts = 0): array + { + $properties = [ + 'content_type' => 'application/json', + 'delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT, + ]; + + if ($correlationId = json_decode($payload, true)['id'] ?? null) { + $properties['correlation_id'] = $correlationId; + } + + $message = new AMQPMessage($payload, $properties); + + $message->set('application_headers', new AMQPTable([ + 'laravel' => [ + 'attempts' => $attempts, + ], + ])); + + return [ + $message, + $correlationId, + ]; } protected function createPayloadArray($job, $queue, $data = '') diff --git a/tests/Feature/ConnectorTest.php b/tests/Feature/ConnectorTest.php new file mode 100644 index 00000000..2c58874a --- /dev/null +++ b/tests/Feature/ConnectorTest.php @@ -0,0 +1,94 @@ +app['config']->set('queue.connections.rabbitmq', [ + 'driver' => 'rabbitmq', + 'queue' => env('RABBITMQ_QUEUE', 'default'), + 'connection' => AMQPLazyConnection::class, + + 'hosts' => [ + [ + 'host' => getenv('HOST'), + 'port' => getenv('PORT'), + 'user' => 'guest', + 'password' => 'guest', + 'vhost' => '/', + ], + ], + + 'options' => [ + 'ssl_options' => [ + 'cafile' => env('RABBITMQ_SSL_CAFILE', null), + 'local_cert' => env('RABBITMQ_SSL_LOCALCERT', null), + 'local_key' => env('RABBITMQ_SSL_LOCALKEY', null), + 'verify_peer' => env('RABBITMQ_SSL_VERIFY_PEER', true), + 'passphrase' => env('RABBITMQ_SSL_PASSPHRASE', null), + ], + ], + + 'worker' => env('RABBITMQ_WORKER', 'default'), + ]); + + /** @var QueueManager $queue */ + $queue = $this->app['queue']; + + /** @var RabbitMQQueue $connection */ + $connection = $queue->connection('rabbitmq'); + + $this->assertInstanceOf(RabbitMQQueue::class, $connection); + $this->assertInstanceOf(AMQPLazyConnection::class, $connection->getConnection()); + $this->assertTrue($connection->getConnection()->isConnected()); + $this->assertTrue($connection->getChannel()->is_open()); + } + + public function testSslConnection(): void + { + $this->app['config']->set('queue.connections.rabbitmq', [ + 'driver' => 'rabbitmq', + 'queue' => env('RABBITMQ_QUEUE', 'default'), + 'connection' => AMQPSSLConnection::class, + + 'hosts' => [ + [ + 'host' => getenv('HOST'), + 'port' => getenv('PORT_SSL'), + 'user' => 'guest', + 'password' => 'guest', + 'vhost' => '/', + ], + ], + + 'options' => [ + 'ssl_options' => [ + 'cafile' => getenv('RABBITMQ_SSL_CAFILE'), + 'local_cert' => null, + 'local_key' => null, + 'verify_peer' => true, + 'passphrase' => null, + ], + ], + + 'worker' => env('RABBITMQ_WORKER', 'default'), + ]); + + /** @var QueueManager $queue */ + $queue = $this->app['queue']; + + /** @var RabbitMQQueue $connection */ + $connection = $queue->connection('rabbitmq'); + $this->assertInstanceOf(RabbitMQQueue::class, $connection); + $this->assertInstanceOf(AMQPSSLConnection::class, $connection->getConnection()); + $this->assertTrue($connection->getConnection()->isConnected()); + $this->assertTrue($connection->getChannel()->is_open()); + } +} diff --git a/tests/Feature/QueueTest.php b/tests/Feature/QueueTest.php index 9ff21d0a..15b8acbb 100644 --- a/tests/Feature/QueueTest.php +++ b/tests/Feature/QueueTest.php @@ -2,18 +2,12 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Feature; -use Enqueue\AmqpLib\AmqpContext; use PhpAmqpLib\Connection\AMQPLazyConnection; class QueueTest extends TestCase { public function testConnection(): void { - /** @var AmqpContext $context */ - $context = $this->connection()->getContext(); - - $this->assertInstanceOf(AmqpContext::class, $context); - $this->assertInstanceOf(AMQPLazyConnection::class, $context->getLibChannel()->getConnection()); - $this->assertTrue($context->getLibChannel()->getConnection()->isConnected()); + $this->assertInstanceOf(AMQPLazyConnection::class, $this->connection()->getChannel()->getConnection()); } } diff --git a/tests/Feature/SslQueueTest.php b/tests/Feature/SslQueueTest.php index 54e83c7f..54969a9c 100644 --- a/tests/Feature/SslQueueTest.php +++ b/tests/Feature/SslQueueTest.php @@ -2,7 +2,6 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Feature; -use Enqueue\AmqpLib\AmqpContext; use PhpAmqpLib\Connection\AMQPSSLConnection; /** @@ -12,26 +11,38 @@ class SslQueueTest extends TestCase { protected function getEnvironmentSetUp($app): void { - parent::getEnvironmentSetUp($app); + $app['config']->set('queue.default', 'rabbitmq'); + $app['config']->set('queue.connections.rabbitmq', [ + 'driver' => 'rabbitmq', + 'queue' => 'default', + 'connection' => AMQPSSLConnection::class, - $app['config']->set('queue.connections.rabbitmq.port', getenv('PORT_SSL')); - $app['config']->set('queue.connections.rabbitmq.ssl_params', [ - 'ssl_on' => true, - 'cafile' => getenv('RABBITMQ_SSL_CAFILE'), - 'local_cert' => null, - 'local_key' => null, - 'verify_peer' => false, - 'passphrase' => null, + 'hosts' => [ + [ + 'host' => getenv('HOST'), + 'port' => getenv('PORT_SSL'), + 'vhost' => '/', + 'user' => 'guest', + 'password' => 'guest', + ], + ], + + 'options' => [ + 'ssl_options' => [ + 'cafile' => getenv('RABBITMQ_SSL_CAFILE'), + 'local_cert' => null, + 'local_key' => null, + 'verify_peer' => true, + 'passphrase' => null, + ], + ], + + 'worker' => 'default', ]); } public function testConnection(): void { - /** @var AmqpContext $context */ - $context = $this->connection()->getContext(); - - $this->assertInstanceOf(AmqpContext::class, $context); - $this->assertInstanceOf(AMQPSSLConnection::class, $context->getLibChannel()->getConnection()); - $this->assertTrue($context->getLibChannel()->getConnection()->isConnected()); + $this->assertInstanceOf(AMQPSSLConnection::class, $this->connection()->getChannel()->getConnection()); } } diff --git a/tests/Feature/TestCase.php b/tests/Feature/TestCase.php index 2a01e6b5..60f39b4c 100644 --- a/tests/Feature/TestCase.php +++ b/tests/Feature/TestCase.php @@ -4,48 +4,94 @@ use Illuminate\Support\Facades\Queue; use Illuminate\Support\Str; -use Interop\Queue\Exception\PurgeQueueNotSupportedException; +use PhpAmqpLib\Exception\AMQPProtocolChannelException; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Jobs\RabbitMQJob; +use VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Mocks\TestJob; use VladimirYuldashev\LaravelQueueRabbitMQ\Tests\TestCase as BaseTestCase; abstract class TestCase extends BaseTestCase { /** - * @throws PurgeQueueNotSupportedException + * @throws AMQPProtocolChannelException */ protected function setUp(): void { parent::setUp(); - $this->connection()->declareEverything('default'); - - $this->connection()->getContext()->purgeQueue( - $this->connection()->getContext()->createQueue('default') - ); + if ($this->connection()->isQueueExists()) { + $this->connection()->purge(); + } } - public function testPush(): void + /** + * @throws AMQPProtocolChannelException + */ + protected function tearDown(): void { + if ($this->connection()->isQueueExists()) { + $this->connection()->purge(); + } + $this->assertSame(0, Queue::size()); + parent::tearDown(); + } + + public function testSizeDoesNotThrowExceptionOnUnknownQueue(): void + { + $this->assertEmpty(0, Queue::size(Str::random())); + } + + public function testPopNothing(): void + { + $this->assertNull(Queue::pop('foo')); + } + + public function testPushRaw(): void + { Queue::pushRaw($payload = Str::random()); - $this->assertSame(1, Queue::size()); + sleep(1); + $this->assertSame(1, Queue::size()); $this->assertNotNull($job = Queue::pop()); - + $this->assertSame(0, $job->attempts()); $this->assertInstanceOf(RabbitMQJob::class, $job); $this->assertSame($payload, $job->getRawBody()); + $this->assertNull($job->getJobId()); + $job->delete(); $this->assertSame(0, Queue::size()); } - public function testLater(): void + public function testPush(): void { - $this->assertSame(0, Queue::size()); + Queue::push(new TestJob()); + sleep(1); + + $this->assertSame(1, Queue::size()); + $this->assertNotNull($job = Queue::pop()); + $this->assertSame(0, $job->attempts()); + $this->assertInstanceOf(RabbitMQJob::class, $job); + $this->assertSame(TestJob::class, $job->resolveName()); + $this->assertNotNull($job->getJobId()); + + $payload = $job->payload(); + + $this->assertSame(TestJob::class, $payload['displayName']); + $this->assertSame('Illuminate\Queue\CallQueuedHandler@call', $payload['job']); + $this->assertNull($payload['maxTries']); + $this->assertNull($payload['delay']); + $this->assertNull($payload['timeout']); + $this->assertNull($payload['timeoutAt']); + $this->assertSame($job->getJobId(), $payload['id']); + } + + public function testLaterRaw(): void + { $payload = Str::random(); $data = [Str::random() => Str::random()]; @@ -75,20 +121,172 @@ public function testLater(): void $this->assertSame(0, Queue::size()); } - public function testJobAttempts(): void + public function testLater(): void + { + Queue::later(3, new TestJob()); + + sleep(1); + + $this->assertSame(0, Queue::size()); + $this->assertNull(Queue::pop()); + + sleep(3); + + $this->assertSame(1, Queue::size()); + $this->assertNotNull($job = Queue::pop()); + + $this->assertInstanceOf(RabbitMQJob::class, $job); + + $body = json_decode($job->getRawBody(), true); + + $this->assertSame(TestJob::class, $body['displayName']); + $this->assertSame('Illuminate\Queue\CallQueuedHandler@call', $body['job']); + $this->assertSame(TestJob::class, $body['data']['commandName']); + $this->assertNotNull($job->getJobId()); + + $job->delete(); + + $this->assertSame(0, Queue::size()); + } + + public function testBulk(): void { - $expectedPayload = __METHOD__.microtime(true); + $count = 100; + $jobs = []; + + for ($i = 0; $i < $count; $i++) { + $jobs[$i] = new TestJob($i); + } + + Queue::bulk($jobs); + + sleep(1); + + $this->assertSame($count, Queue::size()); + } + + public function testReleaseRaw(): void + { + Queue::pushRaw($payload = Str::random()); + + sleep(1); + + $this->assertSame(1, Queue::size()); + $this->assertNotNull($job = Queue::pop()); + $this->assertSame(0, $job->attempts()); + + for ($attempt = 1; $attempt <= 3; $attempt++) { + $job->release(); - Queue::pushRaw($expectedPayload); + sleep(1); + + $this->assertSame(1, Queue::size()); + + $job = Queue::pop(); + + $this->assertSame($attempt, $job->attempts()); + } + } + + public function testRelease(): void + { + Queue::push(new TestJob()); + + sleep(1); + + $this->assertSame(1, Queue::size()); + $this->assertNotNull($job = Queue::pop()); + $this->assertSame(0, $job->attempts()); + + for ($attempt = 1; $attempt <= 3; $attempt++) { + $job->release(); + + sleep(1); + + $this->assertSame(1, Queue::size()); + + $job = Queue::pop(); + + $this->assertSame($attempt, $job->attempts()); + } + } + + public function testReleaseWithDelayRaw(): void + { + Queue::pushRaw($payload = Str::random()); + + sleep(1); + + $this->assertSame(1, Queue::size()); + $this->assertNotNull($job = Queue::pop()); + $this->assertSame(0, $job->attempts()); + + for ($attempt = 1; $attempt <= 3; $attempt++) { + $job->release(4); + + sleep(1); + + $this->assertSame(0, Queue::size()); + $this->assertNull(Queue::pop()); + + sleep(4); + + $this->assertSame(1, Queue::size()); + + $job = Queue::pop(); + + $this->assertSame($attempt, $job->attempts()); + } + } + + public function testReleaseInThePast(): void + { + Queue::push(new TestJob()); $job = Queue::pop(); - $this->assertSame(1, $job->attempts()); + $job->release(-3); + + sleep(1); + + $this->assertInstanceOf(RabbitMQJob::class, Queue::pop()); + } + + public function testReleaseAndReleaseWithDelayAttempts(): void + { + Queue::push(new TestJob()); + + sleep(1); + + $this->assertSame(1, Queue::size()); + $this->assertNotNull($job = Queue::pop()); $job->release(); - $job = Queue::pop(); + sleep(1); + + $this->assertNotNull($job = Queue::pop()); + $this->assertSame(1, $job->attempts()); + + $job->release(3); + + sleep(4); + + $this->assertNotNull($job = Queue::pop()); - $this->assertInstanceOf(RabbitMQJob::class, $job); $this->assertSame(2, $job->attempts()); } + + public function testDelete(): void + { + Queue::push(new TestJob()); + + $job = Queue::pop(); + + $job->delete(); + + sleep(1); + + $this->assertSame(0, Queue::size()); + $this->assertNull(Queue::pop()); + } } diff --git a/tests/Mocks/AmqpConnectionFactorySpy.php b/tests/Mocks/AmqpConnectionFactorySpy.php deleted file mode 100644 index dbb16447..00000000 --- a/tests/Mocks/AmqpConnectionFactorySpy.php +++ /dev/null @@ -1,25 +0,0 @@ -i = $i; + } + public function handle(): void { + // } } diff --git a/tests/TestCase.php b/tests/TestCase.php index 0a712bdb..5eea441e 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -2,10 +2,9 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Tests; -use Enqueue\AmqpLib\AmqpConnectionFactory; use Illuminate\Support\Facades\Queue; -use Interop\Amqp\AmqpTopic; use Orchestra\Testbench\TestCase as BaseTestCase; +use PhpAmqpLib\Connection\AMQPLazyConnection; use VladimirYuldashev\LaravelQueueRabbitMQ\LaravelQueueRabbitMQServiceProvider; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue; @@ -23,46 +22,31 @@ protected function getEnvironmentSetUp($app): void $app['config']->set('queue.default', 'rabbitmq'); $app['config']->set('queue.connections.rabbitmq', [ 'driver' => 'rabbitmq', - 'worker' => 'default', - 'dsn' => null, - 'factory_class' => AmqpConnectionFactory::class, - 'host' => getenv('HOST'), - 'port' => getenv('PORT'), - 'vhost' => '/', - 'login' => 'guest', - 'password' => 'guest', 'queue' => 'default', - - 'options' => [ - 'exchange' => [ - 'name' => null, - 'declare' => true, - 'type' => AmqpTopic::TYPE_DIRECT, - 'passive' => false, - 'durable' => true, - 'auto_delete' => false, - 'arguments' => '[]', + 'connection' => AMQPLazyConnection::class, + + 'hosts' => [ + [ + 'host' => getenv('HOST'), + 'port' => getenv('PORT'), + 'vhost' => '/', + 'user' => 'guest', + 'password' => 'guest', ], + ], - 'queue' => [ - 'declare' => true, - 'bind' => true, - 'passive' => false, - 'durable' => true, - 'exclusive' => false, - 'auto_delete' => false, - 'arguments' => '[]', + 'options' => [ + 'ssl_options' => [ + 'cafile' => null, + 'local_cert' => null, + 'local_key' => null, + 'verify_peer' => true, + 'passphrase' => null, ], ], - 'ssl_params' => [ - 'ssl_on' => false, - 'cafile' => null, - 'local_cert' => null, - 'local_key' => null, - 'verify_peer' => true, - 'passphrase' => null, - ], + 'worker' => 'default', + ]); } diff --git a/tests/Unit/Queue/Connectors/RabbitMQConnectorTest.php b/tests/Unit/Queue/Connectors/RabbitMQConnectorTest.php deleted file mode 100644 index 35c94a55..00000000 --- a/tests/Unit/Queue/Connectors/RabbitMQConnectorTest.php +++ /dev/null @@ -1,209 +0,0 @@ -assertTrue($rc->implementsInterface(ConnectorInterface::class)); - } - - public function testCouldBeConstructedWithDispatcherAsFirstArgument(): void - { - new RabbitMQConnector($this->createMock(Dispatcher::class)); - } - - /** - * @throws ReflectionException - */ - public function testThrowsIfFactoryClassIsNotValidClass(): void - { - $this->expectException(LogicException::class); - $this->expectExceptionMessage('The factory_class option has to be valid class that implements "Interop\Amqp\AmqpConnectionFactory"'); - - $connector = new RabbitMQConnector($this->createMock(Dispatcher::class)); - - $connector->connect(['factory_class' => 'invalidClassName']); - } - - /** - * @throws ReflectionException - */ - public function testThrowsIfFactoryClassDoesNotImplementConnectorFactoryInterface(): void - { - $this->expectException(LogicException::class); - $this->expectExceptionMessage('The factory_class option has to be valid class that implements "Interop\Amqp\AmqpConnectionFactory"'); - - $connector = new RabbitMQConnector($this->createMock(Dispatcher::class)); - - $connector->connect(['factory_class' => stdClass::class]); - } - - /** - * @throws ReflectionException - */ - public function testShouldPassExpectedConfigToConnectionFactory(): void - { - $called = false; - - AmqpConnectionFactorySpy::$spy = function ($config) use (&$called): void { - $called = true; - - $this->assertEquals([ - 'dsn' => 'theDsn', - 'host' => 'theHost', - 'port' => 'thePort', - 'user' => 'theLogin', - 'pass' => 'thePassword', - 'vhost' => 'theVhost', - 'ssl_on' => 'theSslOn', - 'ssl_verify' => 'theVerifyPeer', - 'ssl_cacert' => 'theCafile', - 'ssl_cert' => 'theLocalCert', - 'ssl_key' => 'theLocalKey', - 'ssl_passphrase' => 'thePassPhrase', - ], $config); - }; - - $connector = new RabbitMQConnector($this->createMock(Dispatcher::class)); - - $config = $this->createDummyConfig(); - $config['factory_class'] = AmqpConnectionFactorySpy::class; - - $connector->connect($config); - - $this->assertTrue($called); - } - - /** - * @throws ReflectionException - */ - public function testShouldReturnExpectedInstanceOfQueueOnConnect(): void - { - $connector = new RabbitMQConnector($this->createMock(Dispatcher::class)); - - $config = $this->createDummyConfig(); - $config['factory_class'] = AmqpConnectionFactorySpy::class; - - $queue = $connector->connect($config); - - $this->assertInstanceOf(RabbitMQQueue::class, $queue); - } - - /** - * @throws ReflectionException - */ - public function testShouldSetRabbitMqDlxDelayStrategyIfConnectionFactoryImplementsDelayStrategyAwareInterface(): void - { - $connector = new RabbitMQConnector($this->createMock(Dispatcher::class)); - - $called = false; - DelayStrategyAwareAmqpConnectionFactorySpy::$spy = function ($actualStrategy) use (&$called): void { - $this->assertInstanceOf(RabbitMqDlxDelayStrategy::class, $actualStrategy); - - $called = true; - }; - - $config = $this->createDummyConfig(); - $config['factory_class'] = DelayStrategyAwareAmqpConnectionFactorySpy::class; - - $connector->connect($config); - - $this->assertTrue($called); - } - - /** - * @throws ReflectionException - */ - public function testShouldCallContextCloseMethodOnWorkerStoppingEvent(): void - { - $contextMock = $this->createMock(AmqpContext::class); - $contextMock - ->expects($this->once()) - ->method('close'); - - $dispatcherMock = $this->createMock(Dispatcher::class); - $dispatcherMock - ->expects($this->once()) - ->method('listen') - ->with(WorkerStopping::class, $this->isInstanceOf(Closure::class)) - ->willReturnCallback(static function ($eventName, Closure $listener): void { - $listener(); - }); - - CustomContextAmqpConnectionFactoryMock::$context = $contextMock; - - $connector = new RabbitMQConnector($dispatcherMock); - - $config = $this->createDummyConfig(); - $config['factory_class'] = CustomContextAmqpConnectionFactoryMock::class; - - $connector->connect($config); - } - - /** - * @return array - */ - private function createDummyConfig(): array - { - return [ - 'dsn' => 'theDsn', - 'host' => 'theHost', - 'port' => 'thePort', - 'login' => 'theLogin', - 'password' => 'thePassword', - 'vhost' => 'theVhost', - 'ssl_params' => [ - 'ssl_on' => 'theSslOn', - 'verify_peer' => 'theVerifyPeer', - 'cafile' => 'theCafile', - 'local_cert' => 'theLocalCert', - 'local_key' => 'theLocalKey', - 'passphrase' => 'thePassPhrase', - ], - 'options' => [ - 'exchange' => [ - 'name' => 'anExchangeName', - 'declare' => false, - 'type' => AmqpTopic::TYPE_DIRECT, - 'passive' => false, - 'durable' => true, - 'auto_delete' => false, - ], - - 'queue' => [ - 'name' => 'aQueueName', - 'declare' => false, - 'bind' => false, - 'passive' => false, - 'durable' => true, - 'exclusive' => false, - 'auto_delete' => false, - 'arguments' => '[]', - ], - ], - 'sleep_on_error' => getenv('RABBITMQ_ERROR_SLEEP', 5), - ]; - } -} diff --git a/tests/Unit/Queue/Jobs/RabbitMQJobTest.php b/tests/Unit/Queue/Jobs/RabbitMQJobTest.php deleted file mode 100644 index f03a8645..00000000 --- a/tests/Unit/Queue/Jobs/RabbitMQJobTest.php +++ /dev/null @@ -1,86 +0,0 @@ -assertTrue($rc->implementsInterface(JobContract::class)); - } - - public function testShouldBeSubClassOfQueue(): void - { - $rc = new ReflectionClass(RabbitMQJob::class); - - $this->assertTrue($rc->isSubclassOf(Job::class)); - } - - public function testShouldUseDetectDeadlocksTrait(): void - { - $rc = new ReflectionClass(RabbitMQJob::class); - - $this->assertContains(DetectsLostConnections::class, $rc->getTraitNames()); - } - - public function testCouldBeConstructedWithExpectedArguments(): void - { - $queue = $this->createMock(AmqpQueue::class); - $queue - ->expects($this->once()) - ->method('getQueueName') - ->willReturn('theQueueName'); - - $consumerMock = $this->createConsumerMock(); - $consumerMock - ->expects($this->once()) - ->method('getQueue') - ->willReturn($queue); - - $connectionMock = $this->createRabbitMQQueueMock(); - $connectionMock - ->method('getConnectionName') - ->willReturn('theConnectionName'); - - $job = new RabbitMQJob( - new Container(), - $connectionMock, - $consumerMock, - new AmqpMessage() - ); - - $this->assertSame('theQueueName', $job->getQueue()); - $this->assertSame('theConnectionName', $job->getConnectionName()); - } - - /** - * @return AmqpConsumer|MockObject|AmqpConsumer - */ - private function createConsumerMock() - { - return $this->createMock(AmqpConsumer::class); - } - - /** - * @return MockObject|RabbitMQQueue|RabbitMQQueue - */ - private function createRabbitMQQueueMock() - { - return $this->createMock(RabbitMQQueue::class); - } -} diff --git a/tests/Unit/Queue/RabbitMQQueueTest.php b/tests/Unit/Queue/RabbitMQQueueTest.php deleted file mode 100644 index 7128cce4..00000000 --- a/tests/Unit/Queue/RabbitMQQueueTest.php +++ /dev/null @@ -1,377 +0,0 @@ -assertTrue($rc->implementsInterface(QueueContract::class)); - } - - public function testShouldBeSubClassOfQueue(): void - { - $rc = new ReflectionClass(RabbitMQQueue::class); - - $this->assertTrue($rc->isSubclassOf(Queue::class)); - } - - public function testCouldBeConstructedWithExpectedArguments(): void - { - new RabbitMQQueue($this->createAmqpContext(), $this->createDummyConfig()); - } - - public function testShouldGenerateNewCorrelationIdIfNotSet(): void - { - $queue = new RabbitMQQueue($this->createAmqpContext(), $this->createDummyConfig()); - - $firstId = $queue->getCorrelationId(); - $secondId = $queue->getCorrelationId(); - - $this->assertNotEmpty($firstId); - $this->assertNotEmpty($secondId); - $this->assertNotSame($firstId, $secondId); - } - - public function testShouldReturnPreviouslySetCorrelationId(): void - { - $expectedId = 'theCorrelationId'; - - $queue = new RabbitMQQueue($this->createAmqpContext(), $this->createDummyConfig()); - - $queue->setCorrelationId($expectedId); - - $this->assertSame($expectedId, $queue->getCorrelationId()); - $this->assertSame($expectedId, $queue->getCorrelationId()); - } - - public function testShouldAllowGetContextSetInConstructor(): void - { - $context = $this->createAmqpContext(); - - $queue = new RabbitMQQueue($context, $this->createDummyConfig()); - - $this->assertSame($context, $queue->getContext()); - } - - public function testShouldReturnExpectedNumberOfMessages(): void - { - $expectedQueueName = 'theQueueName'; - $queue = $this->createMock(AmqpQueue::class); - $expectedCount = 123321; - - $context = $this->createAmqpContext(); - $context - ->expects($this->once()) - ->method('createTopic') - ->willReturn($this->createMock(AmqpTopic::class)); - $context - ->expects($this->once()) - ->method('createQueue') - ->with($expectedQueueName) - ->willReturn($queue); - $context - ->expects($this->once()) - ->method('declareQueue') - ->with($this->identicalTo($queue)) - ->willReturn($expectedCount); - - $queue = new RabbitMQQueue($context, $this->createDummyConfig()); - $queue->setContainer($this->createDummyContainer()); - - $this->assertSame($expectedCount, $queue->size($expectedQueueName)); - } - - /** - * @throws Exception - */ - public function testShouldSendExpectedMessageOnPushRaw(): void - { - $expectedQueueName = 'theQueueName'; - $expectedBody = 'thePayload'; - $topic = $this->createMock(AmqpTopic::class); - - $queue = $this->createMock(AmqpQueue::class); - $queue->method('getQueueName')->willReturn('theQueueName'); - - $producer = $this->createMock(AmqpProducer::class); - $producer - ->expects($this->once()) - ->method('send') - ->with($this->identicalTo($topic), $this->isInstanceOf(AmqpMessage::class)) - ->willReturnCallback(function ($actualTopic, AmqpMessage $message) use ($expectedQueueName, $expectedBody, $topic): void { - $this->assertSame($topic, $actualTopic); - $this->assertSame($expectedBody, $message->getBody()); - $this->assertSame($expectedQueueName, $message->getRoutingKey()); - $this->assertSame('application/json', $message->getContentType()); - $this->assertSame(AmqpMessage::DELIVERY_MODE_PERSISTENT, $message->getDeliveryMode()); - $this->assertNotEmpty($message->getCorrelationId()); - $this->assertNull($message->getProperty(RabbitMQJob::ATTEMPT_COUNT_HEADERS_KEY)); - }); - $producer - ->expects($this->never()) - ->method('setDeliveryDelay'); - - $context = $this->createAmqpContext(); - $context - ->expects($this->once()) - ->method('createTopic') - ->willReturn($topic); - $context - ->expects($this->once()) - ->method('createMessage') - ->with($expectedBody) - ->willReturn(new \Interop\Amqp\Impl\AmqpMessage($expectedBody)); - - $context - ->expects($this->once()) - ->method('createQueue') - ->with($expectedQueueName) - ->willReturn($queue); - $context - ->expects($this->once()) - ->method('createProducer') - ->willReturn($producer); - - $queue = new RabbitMQQueue($context, $this->createDummyConfig()); - $queue->setContainer($this->createDummyContainer()); - - $queue->pushRaw('thePayload', $expectedQueueName); - } - - /** - * @throws Exception - */ - public function testShouldSetAttemptCountPropIfNotNull(): void - { - $expectedAttempts = 54321; - - $topic = $this->createMock(AmqpTopic::class); - - $producer = $this->createMock(AmqpProducer::class); - $producer - ->expects($this->once()) - ->method('send') - ->with($this->identicalTo($topic), $this->isInstanceOf(AmqpMessage::class)) - ->willReturnCallback(function ($actualTopic, AmqpMessage $message) use ($expectedAttempts): void { - $this->assertSame($expectedAttempts, $message->getProperty(RabbitMQJob::ATTEMPT_COUNT_HEADERS_KEY)); - }); - $producer - ->expects($this->never()) - ->method('setDeliveryDelay'); - - $context = $this->createAmqpContext(); - $context - ->expects($this->once()) - ->method('createTopic') - ->willReturn($topic); - $context - ->expects($this->once()) - ->method('createMessage') - ->with() - ->willReturn(new \Interop\Amqp\Impl\AmqpMessage()); - $context - ->expects($this->once()) - ->method('createQueue') - ->willReturn($this->createMock(AmqpQueue::class)); - $context - ->expects($this->once()) - ->method('createProducer') - ->willReturn($producer); - - $queue = new RabbitMQQueue($context, $this->createDummyConfig()); - $queue->setContainer($this->createDummyContainer()); - - $queue->pushRaw('thePayload', 'aQueue', ['attempts' => $expectedAttempts]); - } - - /** - * @throws Exception - */ - public function testShouldSetDeliveryDelayIfDelayOptionPresent(): void - { - $expectedDelay = 56; - $expectedDeliveryDelay = 56000; - - $topic = $this->createMock(AmqpTopic::class); - - $producer = $this->createMock(AmqpProducer::class); - $producer - ->expects($this->once()) - ->method('send'); - $producer - ->expects($this->once()) - ->method('setDeliveryDelay') - ->with($expectedDeliveryDelay); - - $context = $this->createAmqpContext(); - $context - ->expects($this->once()) - ->method('createTopic') - ->willReturn($topic); - $context - ->expects($this->once()) - ->method('createMessage') - ->with() - ->willReturn(new \Interop\Amqp\Impl\AmqpMessage()); - $context - ->expects($this->once()) - ->method('createQueue') - ->willReturn($this->createMock(AmqpQueue::class)); - $context - ->expects($this->once()) - ->method('createProducer') - ->willReturn($producer); - - $queue = new RabbitMQQueue($context, $this->createDummyConfig()); - $queue->setContainer($this->createDummyContainer()); - - $queue->pushRaw('thePayload', 'aQueue', ['delay' => $expectedDelay]); - } - - public function testShouldReturnNullIfNoMessagesOnQueue(): void - { - $queue = $this->createMock(AmqpQueue::class); - - $consumer = $this->createMock(AmqpConsumer::class); - $consumer - ->expects($this->once()) - ->method('receiveNoWait') - ->willReturn(null); - - $context = $this->createAmqpContext(); - $context - ->expects($this->once()) - ->method('createTopic') - ->willReturn($this->createMock(AmqpTopic::class)); - $context - ->expects($this->once()) - ->method('createQueue') - ->willReturn($queue); - $context - ->expects($this->once()) - ->method('createConsumer') - ->with($this->identicalTo($queue)) - ->willReturn($consumer); - - $queue = new RabbitMQQueue($context, $this->createDummyConfig()); - $queue->setContainer($this->createDummyContainer()); - - $this->assertNull($queue->pop('aQueue')); - } - - public function testShouldReturnRabbitMQJobIfMessageReceivedFromQueue(): void - { - $queue = $this->createMock(AmqpQueue::class); - - $message = new \Interop\Amqp\Impl\AmqpMessage('thePayload'); - - $consumer = $this->createMock(AmqpConsumer::class); - $consumer - ->expects($this->once()) - ->method('receiveNoWait') - ->willReturn($message); - $consumer - ->expects($this->once()) - ->method('getQueue') - ->willReturn($queue); - - $context = $this->createAmqpContext(); - $context - ->expects($this->once()) - ->method('createTopic') - ->willReturn($this->createMock(AmqpTopic::class)); - $context - ->expects($this->once()) - ->method('createQueue') - ->willReturn($queue); - $context - ->expects($this->once()) - ->method('createConsumer') - ->with($this->identicalTo($queue)) - ->willReturn($consumer); - - $queue = new RabbitMQQueue($context, $this->createDummyConfig()); - $queue->setContainer($this->createDummyContainer()); - - $job = $queue->pop('aQueue'); - - $this->assertInstanceOf(RabbitMQJob::class, $job); - } - - /** - * @return AmqpContext|MockObject|AmqpContext - */ - private function createAmqpContext() - { - return $this->createMock(AmqpContext::class); - } - - private function createDummyContainer(): Container - { - return new Container(); - } - - /** - * @return array - */ - private function createDummyConfig(): array - { - return [ - 'dsn' => 'aDsn', - 'host' => 'aHost', - 'port' => 'aPort', - 'login' => 'aLogin', - 'password' => 'aPassword', - 'vhost' => 'aVhost', - 'ssl_params' => [ - 'ssl_on' => 'aSslOn', - 'verify_peer' => 'aVerifyPeer', - 'cafile' => 'aCafile', - 'local_cert' => 'aLocalCert', - 'local_key' => 'aLocalKey', - ], - 'options' => [ - 'exchange' => [ - 'name' => 'anExchangeName', - 'declare' => false, - 'type' => AmqpTopic::TYPE_DIRECT, - 'passive' => false, - 'durable' => true, - 'auto_delete' => false, - ], - - 'queue' => [ - 'name' => 'aQueueName', - 'declare' => false, - 'bind' => false, - 'passive' => false, - 'durable' => true, - 'exclusive' => false, - 'auto_delete' => false, - 'arguments' => '[]', - ], - ], - 'sleep_on_error' => false, - ]; - } -} From aa487f0780c5c47e9c1978884eaf6ed3d68e9b87 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Sun, 1 Dec 2019 00:31:01 +0300 Subject: [PATCH 118/375] add CHANGELOG.md --- CHANGELOG.md | 15 +++++++++++++++ README.md | 2 ++ 2 files changed, 17 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..37858754 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,15 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +## [10.0.0 (2019-xx-xx)](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v9.0...master) + +- Switch from enqueue to [php-amqplib](https://github.com/php-amqplib/php-amqplib) +- Fix #235 +- Add support for multiple hosts +- Added `exchange:declare` artisan command +- Added `queue:bind` artisan command +- Added `queue:declare` artisan command +- Added `queue:purge` artisan command +- Bulk push messages using `batch_basic_publish` +- No more “sleeps”. Exception will be thrown on lost connection or if any other exception occurs and process manager should be configured properly to manage such situations. diff --git a/README.md b/README.md index b6455923..1a197721 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,8 @@ RabbitMQ Queue driver for Laravel ## Support Policy +Only the latest version will get new features. Bug fixes will be provided using the following scheme: + | Package Version | Laravel Version | Bug Fixes Until | |-----------------|-----------------|---------------------| | 6.0 | 5.5 | August 30th, 2019 | From bf07eaf5c1a032f2b4f8eab5015dd36e9862fe45 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Sun, 1 Dec 2019 00:43:10 +0300 Subject: [PATCH 119/375] remove explicit dependencies --- .gitattributes | 2 +- composer.json | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/.gitattributes b/.gitattributes index 9e5277b7..ff1a3cb7 100644 --- a/.gitattributes +++ b/.gitattributes @@ -8,7 +8,7 @@ .styleci.yml export-ignore .travis.yml export-ignore .php_cs.dist export-ignore -CHANGELOG-* export-ignore +CHANGELOG.md export-ignore CODE_OF_CONDUCT.md export-ignore CONTRIBUTING.md export-ignore phpunit.xml.dist export-ignore diff --git a/composer.json b/composer.json index 32f1a5e2..779064c3 100644 --- a/composer.json +++ b/composer.json @@ -11,14 +11,11 @@ "require": { "php": "^7.2", "ext-json": "*", - "illuminate/database": "^6.0", - "illuminate/support": "^6.0", "illuminate/queue": "^6.0", "php-amqplib/php-amqplib": "^2.11" }, "require-dev": { "phpunit/phpunit": "^8.4", - "illuminate/events": "^6.0", "mockery/mockery": "^1.0", "laravel/horizon": "^3.0", "friendsofphp/php-cs-fixer": "^2.16", From f7315106dcbc1721d4a26ba261cc8d4baaf6fad4 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Wed, 4 Dec 2019 16:43:22 +0300 Subject: [PATCH 120/375] add documentation links to support policy --- README.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 1a197721..52d5000b 100644 --- a/README.md +++ b/README.md @@ -10,14 +10,15 @@ RabbitMQ Queue driver for Laravel Only the latest version will get new features. Bug fixes will be provided using the following scheme: -| Package Version | Laravel Version | Bug Fixes Until | -|-----------------|-----------------|---------------------| -| 6.0 | 5.5 | August 30th, 2019 | -| 7.0 | 5.6 | August 7th, 2018 | -| 7.1 | 5.7 | March 4th, 2019 | -| 7.2 | 5.8 | August 26th, 2019 | -| 9 | 6 | September 3rd, 2021 | -| 10 | 6 | September 3rd, 2021 | +| Package Version | Laravel Version | Bug Fixes Until | | +|-----------------|-----------------|---------------------|---------------------------------------------------------------------------------------------| +| 6.0 | 5.5 | August 30th, 2019 | [Documentation](https://github.com/vyuldashev/laravel-queue-rabbitmq/blob/v6.0/README.md) | +| 7.0 | 5.6 | August 7th, 2018 | [Documentation](https://github.com/vyuldashev/laravel-queue-rabbitmq/blob/v7.0/README.md) | +| 7.1 | 5.7 | March 4th, 2019 | [Documentation](https://github.com/vyuldashev/laravel-queue-rabbitmq/blob/v7.0/README.md) | +| 7.2 | 5.8 | August 26th, 2019 | [Documentation](https://github.com/vyuldashev/laravel-queue-rabbitmq/blob/v7.0/README.md) | +| 8.0 | 5.8 | August 26th, 2019 | [Documentation](https://github.com/vyuldashev/laravel-queue-rabbitmq/blob/v8.0/README.md) | +| 9 | 6 | September 3rd, 2021 | [Documentation](https://github.com/vyuldashev/laravel-queue-rabbitmq/blob/v9.0/README.md) | +| 10 | 6 | September 3rd, 2021 | [Documentation](https://github.com/vyuldashev/laravel-queue-rabbitmq/blob/master/README.md) | ## Installation From cc219d01f70427227b792321274819bd683fde3b Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Fri, 6 Dec 2019 02:26:47 +0300 Subject: [PATCH 121/375] update readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 52d5000b..4a82efc9 100644 --- a/README.md +++ b/README.md @@ -28,9 +28,9 @@ You can install this package via composer using this command: composer require vladimir-yuldashev/laravel-queue-rabbitmq ``` -The package will automatically register itself using Laravel auto-discovery. +The package will automatically register itself. -Setup connection in `config/queue.php` +Add connection to `config/queue.php`: ```php 'connections' => [ From ac9843047ea53d5d0410de5abddd47aed5a3860d Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Fri, 6 Dec 2019 02:32:34 +0300 Subject: [PATCH 122/375] small reorder --- src/Queue/RabbitMQQueue.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index 8e29e6ed..08e386cc 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -345,14 +345,6 @@ public function reject(RabbitMQJob $job, bool $requeue = false): void $this->channel->basic_reject($job->getRabbitMQMessage()->getDeliveryTag(), $requeue); } - /** - * @throws Exception - */ - public function close(): void - { - $this->connection->close(); - } - protected function createMessage($payload, int $attempts = 0): array { $properties = [ @@ -394,4 +386,12 @@ protected function getRandomId(): string { return Str::random(32); } + + /** + * @throws Exception + */ + public function close(): void + { + $this->connection->close(); + } } From 3169ae18c342d19ac80af9886b07fa827bd348e1 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Thu, 12 Dec 2019 20:56:24 +0300 Subject: [PATCH 123/375] update CHANGELOG.md --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 37858754..d17900e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,10 +2,10 @@ All notable changes to this project will be documented in this file. -## [10.0.0 (2019-xx-xx)](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v9.0...master) +## [10.0.0 (2019-12-12)](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v9.0...master) - Switch from enqueue to [php-amqplib](https://github.com/php-amqplib/php-amqplib) -- Fix #235 +- Fix [#235](https://github.com/vyuldashev/laravel-queue-rabbitmq/issues/235) - Add support for multiple hosts - Added `exchange:declare` artisan command - Added `queue:bind` artisan command From 9308d90117428bdbb6df257049d242f955b83e5f Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Fri, 13 Dec 2019 00:52:45 +0300 Subject: [PATCH 124/375] Add missing container instance and connectionName to RabbitMQJob --- CHANGELOG.md | 6 +++++- src/Queue/Jobs/RabbitMQJob.php | 5 +++++ src/Queue/RabbitMQQueue.php | 8 +++++++- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d17900e7..2eae933f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,11 @@ All notable changes to this project will be documented in this file. -## [10.0.0 (2019-12-12)](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v9.0...master) +## [10.0.1 (2019-12-13)](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v10.0.0...v10.0.1) + +- Add missing container instance and connectionName to RabbitMQJob + +## [10.0.0 (2019-12-12)](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v9.0...v10.0.0) - Switch from enqueue to [php-amqplib](https://github.com/php-amqplib/php-amqplib) - Fix [#235](https://github.com/vyuldashev/laravel-queue-rabbitmq/issues/235) diff --git a/src/Queue/Jobs/RabbitMQJob.php b/src/Queue/Jobs/RabbitMQJob.php index d851d211..410098c6 100644 --- a/src/Queue/Jobs/RabbitMQJob.php +++ b/src/Queue/Jobs/RabbitMQJob.php @@ -2,6 +2,7 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Jobs; +use Illuminate\Container\Container; use Illuminate\Contracts\Container\BindingResolutionException; use Illuminate\Contracts\Queue\Job as JobContract; use Illuminate\Queue\Jobs\Job; @@ -35,12 +36,16 @@ class RabbitMQJob extends Job implements JobContract protected $decoded; public function __construct( + Container $container, RabbitMQQueue $rabbitmq, AMQPMessage $message, + string $connectionName, string $queue ) { + $this->container = $container; $this->rabbitmq = $rabbitmq; $this->message = $message; + $this->connectionName = $connectionName; $this->queue = $queue; $this->decoded = $this->payload(); } diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index 08e386cc..7abc0141 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -198,7 +198,13 @@ public function pop($queue = null) /** @var AMQPMessage|null $message */ if ($message = $this->channel->basic_get($queue)) { - return new RabbitMQJob($this, $message, $queue); + return new RabbitMQJob( + $this->container, + $this, + $message, + $this->connectionName, + $queue + ); } } catch (AMQPProtocolChannelException $exception) { // if there is not exchange or queue AMQP will throw exception with code 404 From 08d177a677bce992f0df8e91d4eafa8d0db771ef Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Fri, 13 Dec 2019 02:37:08 +0300 Subject: [PATCH 125/375] fix #235 --- CHANGELOG.md | 4 ++++ src/Queue/RabbitMQQueue.php | 13 ++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2eae933f..df37c1c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ All notable changes to this project will be documented in this file. +## [10.0.2 (2019-12-13)](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v10.0.1...v10.0.2) + +- Finally fix [#235](https://github.com/vyuldashev/laravel-queue-rabbitmq/issues/235) + ## [10.0.1 (2019-12-13)](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v10.0.0...v10.0.1) - Add missing container instance and connectionName to RabbitMQJob diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index 7abc0141..424cea08 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -60,6 +60,13 @@ class RabbitMQQueue extends Queue implements QueueContract */ protected $boundQueues = []; + /** + * Current job being processed. + * + * @var RabbitMQJob + */ + protected $currentJob; + public function __construct( AbstractConnection $connection, string $default @@ -198,7 +205,7 @@ public function pop($queue = null) /** @var AMQPMessage|null $message */ if ($message = $this->channel->basic_get($queue)) { - return new RabbitMQJob( + return $this->currentJob = new RabbitMQJob( $this->container, $this, $message, @@ -398,6 +405,10 @@ protected function getRandomId(): string */ public function close(): void { + if ($this->currentJob) { + $this->reject($this->currentJob); + } + $this->connection->close(); } } From 011a0d472570f48eaf002ef5542fd61057906118 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Mon, 16 Dec 2019 13:37:13 +0300 Subject: [PATCH 126/375] `rabbitmq:consume` command (#289) add `rabbitmq:consume` command which uses `basic_consume` instead of `basic_get` which outperforms `queue:work` by ~2x --- CHANGELOG.md | 5 + composer.json | 3 + src/Console/ConsumeCommand.php | 51 ++++++ src/Consumer.php | 177 ++++++++++++++++++++ src/LaravelQueueRabbitMQServiceProvider.php | 23 +++ src/Queue/Connectors/RabbitMQConnector.php | 7 +- src/Queue/RabbitMQQueue.php | 11 +- 7 files changed, 273 insertions(+), 4 deletions(-) create mode 100644 src/Console/ConsumeCommand.php create mode 100644 src/Consumer.php diff --git a/CHANGELOG.md b/CHANGELOG.md index df37c1c4..30328a4e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ All notable changes to this project will be documented in this file. +## [10.1.0 (2019-12-16)](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v10.0.2...v10.1.0) + +- Add `rabbitmq:consume` command which uses `basic_consume` instead of `basic_get` used by `queue:work`. [#289](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/289) +- Heartbeat disabled globally + ## [10.0.2 (2019-12-13)](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v10.0.1...v10.0.2) - Finally fix [#235](https://github.com/vyuldashev/laravel-queue-rabbitmq/issues/235) diff --git a/composer.json b/composer.json index 779064c3..3c131f16 100644 --- a/composer.json +++ b/composer.json @@ -41,6 +41,9 @@ ] } }, + "suggest": { + "ext-pcntl": "Required to use all features of the queue consumer." + }, "scripts": { "test": [ "@test:style", diff --git a/src/Console/ConsumeCommand.php b/src/Console/ConsumeCommand.php new file mode 100644 index 00000000..554d6981 --- /dev/null +++ b/src/Console/ConsumeCommand.php @@ -0,0 +1,51 @@ +worker; + + $consumer->setContainer($this->laravel); + $consumer->setConsumerTag($this->consumerTag()); + $consumer->setPrefetchSize((int) $this->option('prefetch-size')); + $consumer->setPrefetchCount((int) $this->option('prefetch-count')); + + parent::handle(); + } + + protected function consumerTag(): string + { + if ($consumerTag = $this->option('consumer-tag')) { + return $consumerTag; + } + + return Str::slug(config('app.name', 'laravel'), '_').'_'.getmypid(); + } +} diff --git a/src/Consumer.php b/src/Consumer.php new file mode 100644 index 00000000..e331d554 --- /dev/null +++ b/src/Consumer.php @@ -0,0 +1,177 @@ +container = $value; + } + + public function setConsumerTag(string $value): void + { + $this->consumerTag = $value; + } + + public function setPrefetchSize(int $value): void + { + $this->prefetchSize = $value; + } + + public function setPrefetchCount(int $value): void + { + $this->prefetchCount = $value; + } + + public function daemon($connectionName, $queue, WorkerOptions $options): void + { + if ($this->supportsAsyncSignals()) { + $this->listenForSignals(); + } + + $lastRestart = $this->getTimestampOfLastQueueRestart(); + + /** @var RabbitMQQueue $connection */ + $connection = $this->manager->connection($connectionName); + + $this->channel = $connection->getChannel(); + + $this->channel->basic_qos( + $this->prefetchSize, + $this->prefetchCount, + null + ); + + $this->channel->basic_consume( + $queue, + $this->consumerTag, + false, + false, + false, + false, + function (AMQPMessage $message) use ($connection, $options, $connectionName, $queue): void { + $job = new RabbitMQJob( + $this->container, + $connection, + $message, + $connectionName, + $queue + ); + + if ($this->supportsAsyncSignals()) { + $this->registerTimeoutHandler($job, $options); + } + + $this->runJob($job, $connectionName, $options); + } + ); + + while ($this->channel->is_consuming()) { + // Before reserving any jobs, we will make sure this queue is not paused and + // if it is we will just pause this worker for a given amount of time and + // make sure we do not need to kill this worker process off completely. + if (! $this->daemonShouldRun($options, $connectionName, $queue)) { + $this->pauseWorker($options, $lastRestart); + continue; + } + + // If the daemon should run (not in maintenance mode, etc.), then we can run + // fire off this job for processing. Otherwise, we will need to sleep the + // worker so no more jobs are processed until they should be processed. + try { + $this->channel->wait(null, true, (int) $options->timeout); + } catch (AMQPRuntimeException $exception) { + $this->exceptions->report($exception); + + $this->kill(1); + } catch (Exception $exception) { + $this->exceptions->report($exception); + + $this->stopWorkerIfLostConnection($exception); + } catch (Throwable $exception) { + $this->exceptions->report($exception = new FatalThrowableError($exception)); + + $this->stopWorkerIfLostConnection($exception); + } + + // Finally, we will check to see if we have exceeded our memory limits or if + // the queue should restart based on other indications. If so, we'll stop + // this worker and let whatever is "monitoring" it restart the process. + $this->stopIfNecessary($options, $lastRestart, null); + } + } + + /** + * Determine if the daemon should process on this iteration. + * + * @param WorkerOptions $options + * @param string $connectionName + * @param string $queue + * @return bool + */ + protected function daemonShouldRun(WorkerOptions $options, $connectionName, $queue): bool + { + return ! ((($this->isDownForMaintenance)() && ! $options->force) || $this->paused); + } + + /** + * Mark the given job as failed if it has exceeded the maximum allowed attempts. + * + * @param string $connectionName + * @param Job|RabbitMQJob $job + * @param int $maxTries + * @param Exception $e + */ + protected function markJobAsFailedIfWillExceedMaxAttempts($connectionName, $job, $maxTries, $e): void + { + parent::markJobAsFailedIfWillExceedMaxAttempts($connectionName, $job, $maxTries, $e); + + if (! $job->isDeletedOrReleased()) { + $job->getRabbitMQ()->reject($job); + } + } + + /** + * Stop listening and bail out of the script. + * + * @param int $status + * @return void + */ + public function stop($status = 0): void + { + // Tell the server you are going to stop consuming. + // It will finish up the last message and not send you any more. + $this->channel->basic_cancel($this->consumerTag, false, true); + + parent::stop($status); + } +} diff --git a/src/LaravelQueueRabbitMQServiceProvider.php b/src/LaravelQueueRabbitMQServiceProvider.php index c5c66b4b..2cc3f235 100644 --- a/src/LaravelQueueRabbitMQServiceProvider.php +++ b/src/LaravelQueueRabbitMQServiceProvider.php @@ -2,8 +2,10 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ; +use Illuminate\Contracts\Debug\ExceptionHandler; use Illuminate\Queue\QueueManager; use Illuminate\Support\ServiceProvider; +use VladimirYuldashev\LaravelQueueRabbitMQ\Console\ConsumeCommand; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Connectors\RabbitMQConnector; class LaravelQueueRabbitMQServiceProvider extends ServiceProvider @@ -21,11 +23,32 @@ public function register(): void ); if ($this->app->runningInConsole()) { + $this->app->singleton('rabbitmq.consumer', function () { + $isDownForMaintenance = function () { + return $this->app->isDownForMaintenance(); + }; + + return new Consumer( + $this->app['queue'], + $this->app['events'], + $this->app[ExceptionHandler::class], + $isDownForMaintenance + ); + }); + + $this->app->singleton(ConsumeCommand::class, static function ($app) { + return new ConsumeCommand( + $app['rabbitmq.consumer'], + $app['cache.store'] + ); + }); + $this->commands([ Console\ExchangeDeclareCommand::class, Console\QueueBindCommand::class, Console\QueueDeclareCommand::class, Console\QueuePurgeCommand::class, + Console\ConsumeCommand::class, ]); } } diff --git a/src/Queue/Connectors/RabbitMQConnector.php b/src/Queue/Connectors/RabbitMQConnector.php index 79b25e0a..c18cb980 100644 --- a/src/Queue/Connectors/RabbitMQConnector.php +++ b/src/Queue/Connectors/RabbitMQConnector.php @@ -71,8 +71,13 @@ protected function createConnection(array $config): AbstractConnection /** @var AbstractConnection $connection */ $connection = Arr::get($config, 'connection', AMQPLazyConnection::class); + $hosts = Arr::shuffle(Arr::get($config, 'hosts', [])); + + // manually disable heartbeat so long-running tasks will not fail + $config['options']['heartbeat'] = 0; + return $connection::create_connection( - Arr::get($config, 'hosts', []), + $hosts, $this->filter(Arr::get($config, 'options', [])) ); } diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index 424cea08..07996dbb 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -4,6 +4,7 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Queue; +use ErrorException; use Exception; use Illuminate\Contracts\Queue\Queue as QueueContract; use Illuminate\Queue\Queue; @@ -21,7 +22,7 @@ class RabbitMQQueue extends Queue implements QueueContract /** * The RabbitMQ connection instance. * - * @var AMQPChannel + * @var AbstractConnection */ protected $connection; @@ -405,10 +406,14 @@ protected function getRandomId(): string */ public function close(): void { - if ($this->currentJob) { + if ($this->currentJob && ! $this->currentJob->isDeletedOrReleased()) { $this->reject($this->currentJob); } - $this->connection->close(); + try { + $this->connection->close(); + } catch (ErrorException $exception) { + // Ignore the exception + } } } From 600cfd0b04a72e628120ce055cb4597de7785435 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Mon, 16 Dec 2019 13:40:32 +0300 Subject: [PATCH 127/375] Update README --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index 4a82efc9..3e0318c9 100644 --- a/README.md +++ b/README.md @@ -89,6 +89,14 @@ For Lumen usage the service provider should be registered manually as follow in $app->register(VladimirYuldashev\LaravelQueueRabbitMQ\LaravelQueueRabbitMQServiceProvider::class); ``` +## Consuming Messages + +There are two ways of consuming messages. + +1. `queue:work` command which is Laravel's built-in command. This command utilizes `basic_get`. + +2. `rabbitmq:consume` command which is provided by this package. This command utilizes `basic_consume` and is more performant than `basic_get` by ~2x. + ## Testing Setup RabbitMQ using `docker-compose`: From ad2e9b93f10c9bc299dd239182ba82d88672c083 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Mon, 16 Dec 2019 20:21:49 +0300 Subject: [PATCH 128/375] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 30328a4e..cf8873b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ All notable changes to this project will be documented in this file. - Add `rabbitmq:consume` command which uses `basic_consume` instead of `basic_get` used by `queue:work`. [#289](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/289) - Heartbeat disabled globally +- Shuffle hosts before connecting to get better load balancing ## [10.0.2 (2019-12-13)](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v10.0.1...v10.0.2) From aaaea315fdf2866357e4ce070776e295047a9d41 Mon Sep 17 00:00:00 2001 From: MorrisonHotel Date: Wed, 18 Dec 2019 20:45:55 +0300 Subject: [PATCH 129/375] Exchange type should be an option, not argument (#293) --- src/Console/ExchangeDeclareCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Console/ExchangeDeclareCommand.php b/src/Console/ExchangeDeclareCommand.php index 48cafaf1..88bbed71 100644 --- a/src/Console/ExchangeDeclareCommand.php +++ b/src/Console/ExchangeDeclareCommand.php @@ -35,7 +35,7 @@ public function handle(RabbitMQConnector $connector): void $queue->declareExchange( $this->argument('name'), - $this->argument('type'), + $this->option('type'), (bool) $this->option('durable'), (bool) $this->option('auto-delete') ); From a8fc17552cb88c0b58b8a9567c648ec1c1869ea7 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Wed, 18 Dec 2019 20:47:49 +0300 Subject: [PATCH 130/375] 10.1.1 release notes --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cf8873b4..42978e98 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ All notable changes to this project will be documented in this file. +## [10.1.1 (2019-12-18)](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v10.1.0...v10.1.1) + +- Fix `rabbitmq:exchange-declare` command. [#293](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/293) + ## [10.1.0 (2019-12-16)](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v10.0.2...v10.1.0) - Add `rabbitmq:consume` command which uses `basic_consume` instead of `basic_get` used by `queue:work`. [#289](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/289) From 54c63a1fa14d7ca16ef8ae5f2656ba931ce2008a Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Thu, 19 Dec 2019 14:46:42 +0300 Subject: [PATCH 131/375] add LICENSE.md --- LICENSE.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 LICENSE.md diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 00000000..07fbf64c --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) Vladimir Yuldashev + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. From 39a147d9404832bea82e173b816bde9488e43f65 Mon Sep 17 00:00:00 2001 From: MorrisonHotel Date: Tue, 24 Dec 2019 13:45:29 +0300 Subject: [PATCH 132/375] Fix rabbitmq:queue-bind command (#294) --- src/Console/QueueBindCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Console/QueueBindCommand.php b/src/Console/QueueBindCommand.php index 47c4ea3a..686a3430 100644 --- a/src/Console/QueueBindCommand.php +++ b/src/Console/QueueBindCommand.php @@ -12,7 +12,7 @@ class QueueBindCommand extends Command {queue} {exchange} {connection=rabbitmq : The name of the queue connection to use} - {--routing-key}'; + {--routing-key= : Bind queue to exchange via routing key}'; protected $description = 'Bind queue to exchange'; From 83255a2f815a5473161aec1f82ab87bc9d9894f1 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Tue, 24 Dec 2019 13:47:46 +0300 Subject: [PATCH 133/375] Update changelog for 10.1.2 --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 42978e98..a64e8f12 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ All notable changes to this project will be documented in this file. +## [10.1.2 (2019-12-24)](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v10.1.1...v10.1.2) + +- Fix `rabbitmq:queue-bind` command. [#294](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/294) + ## [10.1.1 (2019-12-18)](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v10.1.0...v10.1.1) - Fix `rabbitmq:exchange-declare` command. [#293](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/293) From 47ad591456232a0ee0bddeb19f00e13e56404d48 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Sun, 12 Jan 2020 01:29:25 +0300 Subject: [PATCH 134/375] fix 100% CPU usage and `stop-when-empty` flag of `rabbitmq:consume` command --- CHANGELOG.md | 5 +++++ src/Consumer.php | 18 ++++++++++++++---- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a64e8f12..30bf8561 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ All notable changes to this project will be documented in this file. +## [10.1.3 (2020-01-12)](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v10.1.2...v10.1.3) + +- Fix 100% CPU usage of `rabbitmq:consume` command by adding sleep to consumer when no messages are got from the queue. +- Fix `stop-on-empty` flag for `rabbitmq:consume` command. + ## [10.1.2 (2019-12-24)](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v10.1.1...v10.1.2) - Fix `rabbitmq:queue-bind` command. [#294](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/294) diff --git a/src/Consumer.php b/src/Consumer.php index e331d554..2393e5f0 100644 --- a/src/Consumer.php +++ b/src/Consumer.php @@ -32,6 +32,9 @@ class Consumer extends Worker /** @var AMQPChannel */ protected $channel; + /** @var boolean */ + protected $gotJob = false; + public function setContainer(Container $value): void { $this->container = $value; @@ -79,6 +82,8 @@ public function daemon($connectionName, $queue, WorkerOptions $options): void false, false, function (AMQPMessage $message) use ($connection, $options, $connectionName, $queue): void { + $this->gotJob = true; + $job = new RabbitMQJob( $this->container, $connection, @@ -104,9 +109,7 @@ function (AMQPMessage $message) use ($connection, $options, $connectionName, $qu continue; } - // If the daemon should run (not in maintenance mode, etc.), then we can run - // fire off this job for processing. Otherwise, we will need to sleep the - // worker so no more jobs are processed until they should be processed. + // If the daemon should run (not in maintenance mode, etc.), then we can wait for a job. try { $this->channel->wait(null, true, (int) $options->timeout); } catch (AMQPRuntimeException $exception) { @@ -123,10 +126,17 @@ function (AMQPMessage $message) use ($connection, $options, $connectionName, $qu $this->stopWorkerIfLostConnection($exception); } + // If no job is got off the queue, we will need to sleep the worker. + if (! $this->gotJob) { + $this->sleep($options->sleep); + } + // Finally, we will check to see if we have exceeded our memory limits or if // the queue should restart based on other indications. If so, we'll stop // this worker and let whatever is "monitoring" it restart the process. - $this->stopIfNecessary($options, $lastRestart, null); + $this->stopIfNecessary($options, $lastRestart, $this->gotJob ? true : null); + + $this->gotJob = false; } } From 6a7e8618c574cbe622d3b7bd6185877c283fe901 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Sun, 12 Jan 2020 01:30:21 +0300 Subject: [PATCH 135/375] fix 100% CPU usage and `stop-when-empty` flag of `rabbitmq:consume` command --- src/Consumer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Consumer.php b/src/Consumer.php index 2393e5f0..286aa429 100644 --- a/src/Consumer.php +++ b/src/Consumer.php @@ -32,7 +32,7 @@ class Consumer extends Worker /** @var AMQPChannel */ protected $channel; - /** @var boolean */ + /** @var bool */ protected $gotJob = false; public function setContainer(Container $value): void From ec6d36bbb67e2239851418efbac2d7679f92cd0b Mon Sep 17 00:00:00 2001 From: Emiel Bom <11626777+adm-bome@users.noreply.github.com> Date: Tue, 11 Feb 2020 12:14:37 +0100 Subject: [PATCH 136/375] Fix laravel job attempts, when removing DLX (#304) * Feature: Alter the delay-strategy from config. - manage the delay-strategy from config. - add backoff-strategy on delayed messages. - add prioritization of delayed messages. * Correct uneccesary git deletions (for readability) * moved: delay config to the end of the file * Fixed the updating of attempts when pushing the job back to the queue * Fixed losing messages when no delay was set * Cleanup fork * Cleanup fork * Code style cleanup * Code style cleanup * Ditched the xDeathCount because the value is not a controlled value. The value can mess things up. The attempts() method was designed to return the current attempt value, not the value of the previous run. Else the worker will try 1 try to long. * Fixed Tests The attempts() method on the Job is/was interpreted wrong. the method mus return the previous_attempt + 1 = current attempt. * Added prioritization when messages put back onto the queue when relaesed. Previously this was handled by rabbitmq with a DLX. [removed] * Fixed Style issues * - Added exchange publishing. - Added delayed messages with a higher priority when put back to the queue. - Added ability to reroute failed messages - Fixed when to acknowledge or decline messages. - Fixed attempts() when multiple delay values are used. - Fixed death worker loop, when queues are missing. - Fixed issue #305 - Fixed issue #302 - Fixed issue #301 - Fixed issue #299 * - Fixed style issues (not showingup in test:style) * - Altered the config structure * - Updated the readMe * - Fixed style * - Fixed Tests (bump) * - Altered the default priority levels when using this feature * - Fixed test for getQueueMaxPriority() --- README.md | 88 ++++++ src/Console/ExchangeDeclareCommand.php | 2 +- src/Queue/Connectors/RabbitMQConnector.php | 34 ++- src/Queue/Jobs/RabbitMQJob.php | 36 ++- src/Queue/RabbitMQQueue.php | 320 ++++++++++++++++++--- tests/Feature/TestCase.php | 36 ++- tests/Functional/RabbitMQQueueTest.php | 151 ++++++++++ tests/Functional/TestCase.php | 142 +++++++++ tests/TestCase.php | 4 +- 9 files changed, 739 insertions(+), 74 deletions(-) create mode 100644 tests/Functional/RabbitMQQueueTest.php create mode 100644 tests/Functional/TestCase.php diff --git a/README.md b/README.md index 3e0318c9..7db1d753 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,94 @@ Add connection to `config/queue.php`: ], ``` +### Optional Config + +Optionally add queue options to the config of a connection. +Every queue created for this connection, get's the properties. + +When you want to prioritize messages when they were delayed, then this is possible by adding extra options. +- When max-priority is omitted, the max priority is set with 2 when used. + +```php +'connections' => [ + // ... + + 'rabbitmq' => [ + // ... + + 'options' => [ + 'queue' => [ + // ... + + 'prioritize_delayed_messages' => false, + 'queue_max_priority' => 10, + ], + ], + ], + + // ... +], +``` + +When you want to publish messages against an exchange with routing-key's, then this is possible by adding extra options. +- When the exchange is omitted, RabbitMQ will use the `amq.direct` exchange for the routing-key +- When routing-key is omitted the routing-key by default is the `queue` name. +- When using `%s` in the routing-key the queue_name will be substituted. + +> Note: when using exchange with routing-key, u probably create your queues with bindings yourself. + +```php +'connections' => [ + // ... + + 'rabbitmq' => [ + // ... + + 'options' => [ + 'queue' => [ + // ... + + 'exchange' => 'application-x', + 'exchange_type' => 'topic', + 'exchange_routing_key' => '', + ], + ], + ], + + // ... +], +``` + +In Laravel failed jobs are stored into the database. But maybe you want to instruct some other process to also do something with the message. +When you want to instruct RabbitMQ to reroute failed messages to a exchange or a specific queue, then this is possible by adding extra options. +- When the exchange is omitted, RabbitMQ will use the `amq.direct` exchange for the routing-key +- When routing-key is omitted, the routing-key by default the `queue` name is substituted with `'.failed'`. +- When using `%s` in the routing-key the queue_name will be substituted. + +> Note: When using failed_job exchange with routing-key, u probably need to create your exchange/queue with bindings yourself. + +```php +'connections' => [ + // ... + + 'rabbitmq' => [ + // ... + + 'options' => [ + 'queue' => [ + // ... + + 'reroute_failed' => true, + 'failed_exchange' => 'failed-exchange', + 'failed_routing_key' => 'application-x.%s', + ], + ], + ], + + // ... +], +``` + ## Laravel Usage Once you completed the configuration you can use Laravel Queue API. If you used other queue drivers you do not need to change anything else. If you do not know how to use Queue API, please refer to the official Laravel documentation: http://laravel.com/docs/queues diff --git a/src/Console/ExchangeDeclareCommand.php b/src/Console/ExchangeDeclareCommand.php index 88bbed71..955da0a9 100644 --- a/src/Console/ExchangeDeclareCommand.php +++ b/src/Console/ExchangeDeclareCommand.php @@ -40,6 +40,6 @@ public function handle(RabbitMQConnector $connector): void (bool) $this->option('auto-delete') ); - $this->warn('Exchange declared successfully.'); + $this->info('Exchange declared successfully.'); } } diff --git a/src/Queue/Connectors/RabbitMQConnector.php b/src/Queue/Connectors/RabbitMQConnector.php index c18cb980..a3b44b98 100644 --- a/src/Queue/Connectors/RabbitMQConnector.php +++ b/src/Queue/Connectors/RabbitMQConnector.php @@ -38,12 +38,13 @@ public function __construct(Dispatcher $dispatcher) */ public function connect(array $config): Queue { - $connection = $this->createConnection($config); + $connection = $this->createConnection(Arr::except($config, 'options.queue')); $queue = $this->createQueue( Arr::get($config, 'worker', 'default'), $connection, - $config['queue'] + $config['queue'], + Arr::get($config, 'options.queue', []) ); if (! $queue instanceof RabbitMQQueue) { @@ -71,29 +72,42 @@ protected function createConnection(array $config): AbstractConnection /** @var AbstractConnection $connection */ $connection = Arr::get($config, 'connection', AMQPLazyConnection::class); - $hosts = Arr::shuffle(Arr::get($config, 'hosts', [])); - // manually disable heartbeat so long-running tasks will not fail - $config['options']['heartbeat'] = 0; + Arr::set($config, 'options.heartbeat', 0); return $connection::create_connection( - $hosts, + Arr::shuffle(Arr::get($config, 'hosts', [])), $this->filter(Arr::get($config, 'options', [])) ); } - protected function createQueue(string $worker, AbstractConnection $connection, string $queue) + /** + * Create a queue for the worker. + * + * @param string $worker + * @param AbstractConnection $connection + * @param string $queue + * @param array $options + * @return HorizonRabbitMQQueue|RabbitMQQueue|Queue + */ + protected function createQueue(string $worker, AbstractConnection $connection, string $queue, array $options = []) { switch ($worker) { case 'default': - return new RabbitMQQueue($connection, $queue); + return new RabbitMQQueue($connection, $queue, $options); case 'horizon': - return new HorizonRabbitMQQueue($connection, $queue); + return new HorizonRabbitMQQueue($connection, $queue, $options); default: - return new $worker($connection, $queue); + return new $worker($connection, $queue, $options); } } + /** + * Recursively filter only null values. + * + * @param array $array + * @return array + */ private function filter(array $array): array { foreach ($array as $index => &$value) { diff --git a/src/Queue/Jobs/RabbitMQJob.php b/src/Queue/Jobs/RabbitMQJob.php index 410098c6..7f7af1d7 100644 --- a/src/Queue/Jobs/RabbitMQJob.php +++ b/src/Queue/Jobs/RabbitMQJob.php @@ -75,7 +75,7 @@ public function attempts(): int $headers = Arr::get($this->message->get_properties(), 'application_headers'); if (! $headers) { - return 0; + return 1; } $data = $headers->getNativeData(); @@ -83,7 +83,17 @@ public function attempts(): int $laravelAttempts = (int) Arr::get($data, 'laravel.attempts', 0); $xDeathCount = (int) Arr::get($headers->getNativeData(), 'x-death.0.count', 0); - return $laravelAttempts + $xDeathCount; + return ($laravelAttempts) + 1; + } + + public function fail($e = null): void + { + parent::fail($e); + + // We must tel rabbitMQ this Job is failed + // The message must be rejected when the Job marked as failed, in case rabbitMQ wants to do some extra magic. + // like: Death lettering the message to an other exchange/routing-key. + $this->rabbitmq->reject($this); } /** @@ -95,7 +105,11 @@ public function delete(): void { parent::delete(); - $this->rabbitmq->ack($this); + // When delete is called and the Job was not failed, the message must be acknowledged. + // This is because this is a controlled call by a developer. So the message was handled correct. + if (! $this->failed) { + $this->rabbitmq->ack($this); + } // required for Laravel Horizon if ($this->rabbitmq instanceof HorizonRabbitMQQueue) { @@ -108,17 +122,15 @@ public function delete(): void */ public function release($delay = 0): void { - parent::release($delay); - - if ($delay > 0) { - $this->rabbitmq->ack($this); + parent::release(); - $this->rabbitmq->laterRaw($delay, $this->message->body, $this->queue, $this->attempts()); + // Always create a new message when this Job is released + $this->rabbitmq->laterRaw($delay, $this->message->body, $this->queue, $this->attempts()); - return; - } - - $this->rabbitmq->reject($this); + // Releasing a Job means the message was failed to process. + // Because this Job is always recreated and pushed as new message, this Job is correctly handled. + // We must tell rabbitMQ this fact. + $this->rabbitmq->ack($this); } /** diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index 07996dbb..5092ac61 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -8,6 +8,7 @@ use Exception; use Illuminate\Contracts\Queue\Queue as QueueContract; use Illuminate\Queue\Queue; +use Illuminate\Support\Arr; use Illuminate\Support\Str; use PhpAmqpLib\Channel\AMQPChannel; use PhpAmqpLib\Connection\AbstractConnection; @@ -68,13 +69,27 @@ class RabbitMQQueue extends Queue implements QueueContract */ protected $currentJob; + /** + * @var array + */ + protected $options; + + /** + * RabbitMQQueue constructor. + * + * @param AbstractConnection $connection + * @param string $default + * @param array $options + */ public function __construct( AbstractConnection $connection, - string $default + string $default, + array $options = [] ) { $this->connection = $connection; $this->channel = $connection->channel(); $this->default = $default; + $this->options = $options; } /** @@ -100,6 +115,8 @@ public function size($queue = null): int /** * {@inheritdoc} + * + * @throws AMQPProtocolChannelException */ public function push($job, $data = '', $queue = null) { @@ -108,27 +125,38 @@ public function push($job, $data = '', $queue = null) /** * {@inheritdoc} + * + * @throws AMQPProtocolChannelException */ public function pushRaw($payload, $queue = null, array $options = []) { - $queue = $this->getQueue($queue); + $destination = $this->getQueue($queue); + $exchange = $this->getExchange(); - $this->declareExchange($queue); - $this->declareQueue($queue, true, false, [ - 'x-dead-letter-exchange' => $queue, - 'x-dead-letter-routing-key' => $queue, - ]); - $this->bindQueue($queue, $queue, $queue); + // When a exchange is defined and no exchange is present in RabbitMQ, create an exchange. + if ($exchange && ! $this->isExchangeExists($exchange)) { + $this->declareExchange($exchange, $this->getExchangeType()); + } - [$message, $correlationId] = $this->createMessage($payload); + // When no exchange is defined, create a queue for amq.direct publishing, but only if it not already present. + if (! $exchange && ! $this->isQueueExists($destination)) { + $this->declareQueue($destination, true, false, $this->getQueueArguments($destination)); + } - $this->channel->basic_publish($message, $queue, $queue, true, false); + [$message, $correlationId] = ($attempts = Arr::get($options, 'attempts')) + ? $this->createMessage($payload, $attempts) + : $this->createMessage($payload); + + // Publish the message + $this->channel->basic_publish($message, $exchange, $this->getRoutingKey($destination), true, false); return $correlationId; } /** * {@inheritdoc} + * + * @throws AMQPProtocolChannelException */ public function later($delay, $job, $data = '', $queue = null) { @@ -139,56 +167,62 @@ public function later($delay, $job, $data = '', $queue = null) ); } + /** + * @param $delay + * @param $payload + * @param null $queue + * @param int $attempts + * @return mixed + * + * @throws AMQPProtocolChannelException + */ public function laterRaw($delay, $payload, $queue = null, $attempts = 0) { $ttl = $this->secondsUntil($delay) * 1000; - if ($ttl < 0) { - return $this->pushRaw($payload, $queue, []); + // When no ttl just publish a new message to the exchange or queue + if ($ttl <= 0) { + return $this->pushRaw($payload, $queue, ['delay' => $delay, 'attempts' => $attempts]); } - $destinationQueue = $this->getQueue($queue); - $delayedQueue = $this->getQueue($queue).'.delay.'.$ttl; + $destination = $this->getQueue($queue).'.delay.'.$ttl; - $this->declareExchange($destinationQueue); - $this->declareQueue($destinationQueue, true, false, [ - 'x-dead-letter-exchange' => $destinationQueue, - 'x-dead-letter-routing-key' => $destinationQueue, - ]); - $this->declareQueue($delayedQueue, true, false, [ - 'x-dead-letter-exchange' => $destinationQueue, - 'x-dead-letter-routing-key' => $destinationQueue, - 'x-message-ttl' => $ttl, - ]); - $this->bindQueue($destinationQueue, $destinationQueue, $destinationQueue); + $this->declareQueue($destination, true, false, $this->getDelayQueueArguments($this->getQueue($queue), $ttl)); [$message, $correlationId] = $this->createMessage($payload, $attempts); - $this->channel->basic_publish($message, null, $delayedQueue, true, false); + // Publish directly on the delayQueue, no need to publish trough an exchange. + $this->channel->basic_publish($message, null, $destination, true, false); return $correlationId; } /** * {@inheritdoc} + * + * @throws AMQPProtocolChannelException */ public function bulk($jobs, $data = '', $queue = null): void { - $queue = $this->getQueue($queue); + $destination = $this->getQueue($queue); + $exchange = $this->getExchange(); + + // When a exchange is defined and no exchange is present in RabbitMQ, create an exchange. + if ($exchange && ! $this->isExchangeExists($exchange)) { + $this->declareExchange($exchange, $this->getExchangeType()); + } + + // When no exchange is defined, create a queue for amq.direct publishing, but only if it not already present. + if (! $exchange && ! $this->isQueueExists($destination)) { + $this->declareQueue($destination, true, false, $this->getQueueArguments($destination)); + } foreach ((array) $jobs as $job) { [$message] = $this->createMessage( $this->createPayload($job, $queue, $data) ); - $this->declareExchange($queue); - $this->declareQueue($queue, true, false, [ - 'x-dead-letter-exchange' => $queue, - 'x-dead-letter-routing-key' => $queue, - ]); - $this->bindQueue($queue, $queue, $queue); - - $this->channel->batch_basic_publish($message, $queue, $queue); + $this->channel->batch_basic_publish($message, $exchange, $destination); } $this->channel->publish_batch(); @@ -215,9 +249,14 @@ public function pop($queue = null) ); } } catch (AMQPProtocolChannelException $exception) { - // if there is not exchange or queue AMQP will throw exception with code 404 - // we need to catch it and return null + // If there is not exchange or queue AMQP will throw exception with code 404 + // We need to catch it and return null if ($exception->amqp_reply_code === 404) { + + // Because of the channel exception the channel was closed and removed. + // We have to open a new channel. Because else the worker(s) are stuck in a loop, without processing. + $this->channel = $this->connection->channel(); + return null; } @@ -227,22 +266,36 @@ public function pop($queue = null) return null; } + /** + * @return AbstractConnection + */ public function getConnection(): AbstractConnection { return $this->connection; } + /** + * @return AMQPChannel + */ public function getChannel(): AMQPChannel { return $this->channel; } + /** + * Gets a queue/destination, by default the queue option set on the connection. + * @param null $queue + * @return string + */ public function getQueue($queue = null) { return $queue ?: $this->default; } /** + * Checks if the given exchange already present/defined in RabbitMQ. + * Returns false when when the exchange is missing. + * * @param string $exchange * @return bool * @throws AMQPProtocolChannelException @@ -265,11 +318,21 @@ public function isExchangeExists(string $exchange): bool } } + /** + * Declare a exchange in rabbitMQ. + * + * @param string $name + * @param string $type + * @param bool $durable + * @param bool $autoDelete + * @param array $arguments + */ public function declareExchange( string $name, string $type = AMQPExchangeType::DIRECT, bool $durable = true, - bool $autoDelete = false + bool $autoDelete = false, + array $arguments = [] ): void { if (in_array($name, $this->exchanges, true)) { return; @@ -282,11 +345,15 @@ public function declareExchange( $durable, $autoDelete, false, - true + true, + new AMQPTable($arguments) ); } /** + * Checks if the given queue already present/defined in RabbitMQ. + * Returns false when when the queue is missing. + * * @param string $name * @return bool * @throws AMQPProtocolChannelException @@ -311,6 +378,14 @@ public function isQueueExists(?string $name = null): bool } } + /** + * Declare a queue in rabbitMQ. + * + * @param string $name + * @param bool $durable + * @param bool $autoDelete + * @param array $arguments + */ public function declareQueue(string $name, bool $durable = true, bool $autoDelete = false, array $arguments = []): void { if (in_array($name, $this->queues, true)) { @@ -328,6 +403,11 @@ public function declareQueue(string $name, bool $durable = true, bool $autoDelet ); } + /** + * @param string $queue + * @param string $exchange + * @param string $routingKey + */ public function bindQueue(string $queue, string $exchange, string $routingKey = ''): void { if (in_array( @@ -341,6 +421,9 @@ public function bindQueue(string $queue, string $exchange, string $routingKey = $this->channel->queue_bind($queue, $exchange, $routingKey); } + /** + * @param null $queue + */ public function purge($queue = null): void { // create a temporary channel, so the main channel will not be closed on exception @@ -349,16 +432,32 @@ public function purge($queue = null): void $channel->close(); } + /** + * @param RabbitMQJob $job + */ public function ack(RabbitMQJob $job): void { $this->channel->basic_ack($job->getRabbitMQMessage()->getDeliveryTag()); } + /** + * Reject current Job. + * + * @param RabbitMQJob $job + * @param bool $requeue + */ public function reject(RabbitMQJob $job, bool $requeue = false): void { $this->channel->basic_reject($job->getRabbitMQMessage()->getDeliveryTag(), $requeue); } + /** + * Create a AMQP message. + * + * @param $payload + * @param int $attempts + * @return array + */ protected function createMessage($payload, int $attempts = 0): array { $properties = [ @@ -370,6 +469,10 @@ protected function createMessage($payload, int $attempts = 0): array $properties['correlation_id'] = $correlationId; } + if ($this->isPrioritizeDelayed()) { + $properties['priority'] = $attempts; + } + $message = new AMQPMessage($payload, $properties); $message->set('application_headers', new AMQPTable([ @@ -384,6 +487,14 @@ protected function createMessage($payload, int $attempts = 0): array ]; } + /** + * Create a payload array from the given job and data. + * + * @param object|string $job + * @param string $queue + * @param string $data + * @return array + */ protected function createPayloadArray($job, $queue, $data = '') { return array_merge(parent::createPayloadArray($job, $queue, $data), [ @@ -416,4 +527,135 @@ public function close(): void // Ignore the exception } } + + /** + * Get the Queue arguments. + * + * @return array + */ + protected function getQueueArguments(string $destination): array + { + $arguments = []; + + // Messages without a priority property are treated as if their priority were 0. + // Messages with a priority which is higher than the queue's maximum, are treated as if they were + // published with the maximum priority. + if ($this->isPrioritizeDelayed()) { + $arguments['x-max-priority'] = $this->getQueueMaxPriority(); + } + + if ($this->isRerouteFailed()) { + $arguments['x-dead-letter-exchange'] = $this->getFailedExchange() ?? ''; + $arguments['x-dead-letter-routing-key'] = $this->getFailedRoutingKey($destination); + } + + return $arguments; + } + + /** + * Get the Delay queue arguments. + * + * @param string $destination + * @param int $ttl + * @return array + */ + protected function getDelayQueueArguments(string $destination, int $ttl): array + { + return [ + 'x-dead-letter-exchange' => $this->getExchange() ?? '', + 'x-dead-letter-routing-key' => $this->getRoutingKey($destination), + 'x-message-ttl' => $ttl, + 'x-expires' => $ttl * 2, + ]; + } + + /** + * Returns &true;, if delayed messages should be prioritized. + * + * @return bool + */ + protected function isPrioritizeDelayed(): bool + { + return boolval(Arr::get($this->options, 'prioritize_delayed') ?: false); + } + + /** + * Returns a integer with a default of '2' for when using prioritization on delayed messages. + * If priority queues are desired, we recommend using between 1 and 10. + * Using more priority layers, will consume more CPU resources and would affect runtimes. + * + * @see https://www.rabbitmq.com/priority.html + * @return int + */ + protected function getQueueMaxPriority(): int + { + return intval(Arr::get($this->options, 'queue_max_priority') ?: 2); + } + + /** + * Get the exchange name, or &null; as default value. + * + * @param string $exchange + * @return string|null + */ + protected function getExchange(string $exchange = null): ?string + { + return $exchange ?: Arr::get($this->options, 'exchange') ?: null; + } + + /** + * Get the routing-key for when you use exchanges + * The default routing-key is the given destination. + * + * @param string $destination + * @return string + */ + protected function getRoutingKey(string $destination): string + { + return ltrim(sprintf(Arr::get($this->options, 'exchange_routing_key') ?: '%s', $destination), '.'); + } + + /** + * Get the exchangeType, or AMQPExchangeType::DIRECT as default. + * + * @param string|null $type + * @return string + */ + protected function getExchangeType(string $type = null): string + { + return @constant(AMQPExchangeType::class.'::'.Str::upper($type ?: Arr::get($this->options, 'exchange_type') ?: 'direct')) ?: AMQPExchangeType::DIRECT; + } + + /** + * Returns &true;, if failed messages should be rerouted. + * + * @return bool + */ + protected function isRerouteFailed(): bool + { + return boolval(Arr::get($this->options, 'reroute_failed') ?: false); + } + + /** + * Get the exchange for failed messages. + * + * @param string|null $exchange + * @return string|null + */ + protected function getFailedExchange(string $exchange = null): ?string + { + return $exchange ?: Arr::get($this->options, 'failed_exchange') ?: null; + } + + /** + * Get the routing-key for failed messages + * The default routing-key is the given destination substituted by '.failed'. + * + * @param string $destination + * @return string + */ + protected function getFailedRoutingKey(string $destination): string + { + return ltrim(sprintf(Arr::get($this->options, 'failed_routing_key') ?: '%s.failed', $destination), '.'); + } } diff --git a/tests/Feature/TestCase.php b/tests/Feature/TestCase.php index 60f39b4c..3f7092ed 100644 --- a/tests/Feature/TestCase.php +++ b/tests/Feature/TestCase.php @@ -55,7 +55,7 @@ public function testPushRaw(): void $this->assertSame(1, Queue::size()); $this->assertNotNull($job = Queue::pop()); - $this->assertSame(0, $job->attempts()); + $this->assertSame(1, $job->attempts()); $this->assertInstanceOf(RabbitMQJob::class, $job); $this->assertSame($payload, $job->getRawBody()); @@ -74,7 +74,7 @@ public function testPush(): void $this->assertSame(1, Queue::size()); $this->assertNotNull($job = Queue::pop()); - $this->assertSame(0, $job->attempts()); + $this->assertSame(1, $job->attempts()); $this->assertInstanceOf(RabbitMQJob::class, $job); $this->assertSame(TestJob::class, $job->resolveName()); $this->assertNotNull($job->getJobId()); @@ -173,9 +173,9 @@ public function testReleaseRaw(): void $this->assertSame(1, Queue::size()); $this->assertNotNull($job = Queue::pop()); - $this->assertSame(0, $job->attempts()); + $this->assertSame(1, $job->attempts()); - for ($attempt = 1; $attempt <= 3; $attempt++) { + for ($attempt = 2; $attempt <= 4; $attempt++) { $job->release(); sleep(1); @@ -196,9 +196,9 @@ public function testRelease(): void $this->assertSame(1, Queue::size()); $this->assertNotNull($job = Queue::pop()); - $this->assertSame(0, $job->attempts()); + $this->assertSame(1, $job->attempts()); - for ($attempt = 1; $attempt <= 3; $attempt++) { + for ($attempt = 2; $attempt <= 4; $attempt++) { $job->release(); sleep(1); @@ -219,9 +219,9 @@ public function testReleaseWithDelayRaw(): void $this->assertSame(1, Queue::size()); $this->assertNotNull($job = Queue::pop()); - $this->assertSame(0, $job->attempts()); + $this->assertSame(1, $job->attempts()); - for ($attempt = 1; $attempt <= 3; $attempt++) { + for ($attempt = 2; $attempt <= 4; $attempt++) { $job->release(4); sleep(1); @@ -265,7 +265,7 @@ public function testReleaseAndReleaseWithDelayAttempts(): void sleep(1); $this->assertNotNull($job = Queue::pop()); - $this->assertSame(1, $job->attempts()); + $this->assertSame(2, $job->attempts()); $job->release(3); @@ -273,7 +273,7 @@ public function testReleaseAndReleaseWithDelayAttempts(): void $this->assertNotNull($job = Queue::pop()); - $this->assertSame(2, $job->attempts()); + $this->assertSame(3, $job->attempts()); } public function testDelete(): void @@ -289,4 +289,20 @@ public function testDelete(): void $this->assertSame(0, Queue::size()); $this->assertNull(Queue::pop()); } + + public function testFailed(): void + { + Queue::push(new TestJob()); + + $job = Queue::pop(); + + $job->fail(new \RuntimeException($job->resolveName().' has an exception.')); + + sleep(1); + + $this->assertSame(true, $job->hasFailed()); + $this->assertSame(true, $job->isDeleted()); + $this->assertSame(0, Queue::size()); + $this->assertNull(Queue::pop()); + } } diff --git a/tests/Functional/RabbitMQQueueTest.php b/tests/Functional/RabbitMQQueueTest.php new file mode 100644 index 00000000..4781e19d --- /dev/null +++ b/tests/Functional/RabbitMQQueueTest.php @@ -0,0 +1,151 @@ +connection(); + $this->assertInstanceOf(RabbitMQQueue::class, $queue); + + $queue = $this->connection('rabbitmq-with-options'); + $this->assertInstanceOf(RabbitMQQueue::class, $queue); + + $queue = $this->connection('rabbitmq-with-options-empty'); + $this->assertInstanceOf(RabbitMQQueue::class, $queue); + } + + public function testRerouteFailed(): void + { + /** @var $queue RabbitMQQueue */ + $queue = $this->connection(); + $this->assertFalse($this->callMethod($queue, 'isRerouteFailed')); + + $queue = $this->connection('rabbitmq-with-options'); + $this->assertTrue($this->callMethod($queue, 'isRerouteFailed')); + + $queue = $this->connection('rabbitmq-with-options-empty'); + $this->assertFalse($this->callMethod($queue, 'isRerouteFailed')); + } + + public function testPrioritizeDelayed(): void + { + /** @var $queue RabbitMQQueue */ + $queue = $this->connection(); + $this->assertFalse($this->callMethod($queue, 'isPrioritizeDelayed')); + + $queue = $this->connection('rabbitmq-with-options'); + $this->assertTrue($this->callMethod($queue, 'isPrioritizeDelayed')); + + $queue = $this->connection('rabbitmq-with-options-empty'); + $this->assertFalse($this->callMethod($queue, 'isPrioritizeDelayed')); + } + + public function testQueueMaxPriority(): void + { + /** @var $queue RabbitMQQueue */ + $queue = $this->connection(); + $this->assertIsInt($this->callMethod($queue, 'getQueueMaxPriority')); + $this->assertSame(2, $this->callMethod($queue, 'getQueueMaxPriority')); + + $queue = $this->connection('rabbitmq-with-options'); + $this->assertIsInt($this->callMethod($queue, 'getQueueMaxPriority')); + $this->assertSame(20, $this->callMethod($queue, 'getQueueMaxPriority')); + + $queue = $this->connection('rabbitmq-with-options-empty'); + $this->assertIsInt($this->callMethod($queue, 'getQueueMaxPriority')); + $this->assertSame(2, $this->callMethod($queue, 'getQueueMaxPriority')); + } + + public function testExchangeType(): void + { + /** @var $queue RabbitMQQueue */ + $queue = $this->connection(); + $this->assertSame(AMQPExchangeType::DIRECT, $this->callMethod($queue, 'getExchangeType')); + $this->assertSame(AMQPExchangeType::DIRECT, $this->callMethod($queue, 'getExchangeType', [''])); + $this->assertSame(AMQPExchangeType::DIRECT, $this->callMethod($queue, 'getExchangeType', ['test'])); + $this->assertSame(AMQPExchangeType::TOPIC, $this->callMethod($queue, 'getExchangeType', ['topic'])); + + $queue = $this->connection('rabbitmq-with-options'); + $this->assertSame(AMQPExchangeType::TOPIC, $this->callMethod($queue, 'getExchangeType')); + + $queue = $this->connection('rabbitmq-with-options-empty'); + $this->assertSame(AMQPExchangeType::DIRECT, $this->callMethod($queue, 'getExchangeType')); + } + + public function testExchange(): void + { + /** @var $queue RabbitMQQueue */ + $queue = $this->connection(); + $this->assertSame('test', $this->callMethod($queue, 'getExchange', ['test'])); + $this->assertNull($this->callMethod($queue, 'getExchange', [''])); + $this->assertNull($this->callMethod($queue, 'getExchange')); + + $queue = $this->connection('rabbitmq-with-options'); + $this->assertNotNull($this->callMethod($queue, 'getExchange')); + $this->assertSame('application-x', $this->callMethod($queue, 'getExchange')); + + $queue = $this->connection('rabbitmq-with-options-empty'); + $this->assertNull($this->callMethod($queue, 'getExchange')); + } + + public function testFailedExchange(): void + { + /** @var $queue RabbitMQQueue */ + $queue = $this->connection(); + $this->assertSame('test', $this->callMethod($queue, 'getFailedExchange', ['test'])); + $this->assertNull($this->callMethod($queue, 'getExchange', [''])); + $this->assertNull($this->callMethod($queue, 'getFailedExchange')); + + $queue = $this->connection('rabbitmq-with-options'); + $this->assertNotNull($this->callMethod($queue, 'getFailedExchange')); + $this->assertSame('failed-exchange', $this->callMethod($queue, 'getFailedExchange')); + + $queue = $this->connection('rabbitmq-with-options-empty'); + $this->assertNull($this->callMethod($queue, 'getFailedExchange')); + } + + public function testRoutingKey(): void + { + /** @var $queue RabbitMQQueue */ + $queue = $this->connection(); + $this->assertSame('test', $this->callMethod($queue, 'getRoutingKey', ['test'])); + $this->assertSame('', $this->callMethod($queue, 'getRoutingKey', [''])); + + $queue = $this->connection('rabbitmq-with-options'); + $this->assertSame('process.test', $this->callMethod($queue, 'getRoutingKey', ['test'])); + + $queue = $this->connection('rabbitmq-with-options-empty'); + $this->assertSame('test', $this->callMethod($queue, 'getRoutingKey', ['test'])); + } + + public function testFailedRoutingKey(): void + { + /** @var $queue RabbitMQQueue */ + $queue = $this->connection(); + $this->assertSame('test.failed', $this->callMethod($queue, 'getFailedRoutingKey', ['test'])); + $this->assertSame('failed', $this->callMethod($queue, 'getFailedRoutingKey', [''])); + + $queue = $this->connection('rabbitmq-with-options'); + $this->assertSame('application-x.test.failed', $this->callMethod($queue, 'getFailedRoutingKey', ['test'])); + + $queue = $this->connection('rabbitmq-with-options-empty'); + $this->assertSame('test.failed', $this->callMethod($queue, 'getFailedRoutingKey', ['test'])); + } + + public function testQueueArguments(): void + { + $this->assertTrue(true); + } + + public function testDelayQueueArguments(): void + { + $this->assertTrue(true); + } +} diff --git a/tests/Functional/TestCase.php b/tests/Functional/TestCase.php new file mode 100644 index 00000000..b7f5b48b --- /dev/null +++ b/tests/Functional/TestCase.php @@ -0,0 +1,142 @@ +set('queue.default', 'rabbitmq'); + $app['config']->set('queue.connections.rabbitmq', [ + 'driver' => 'rabbitmq', + 'queue' => 'order', + 'connection' => AMQPLazyConnection::class, + + 'hosts' => [ + [ + 'host' => getenv('HOST'), + 'port' => getenv('PORT'), + 'vhost' => '/', + 'user' => 'guest', + 'password' => 'guest', + ], + ], + + 'options' => [ + 'ssl_options' => [ + 'cafile' => null, + 'local_cert' => null, + 'local_key' => null, + 'verify_peer' => true, + 'passphrase' => null, + ], + ], + + 'worker' => 'default', + + ]); + $app['config']->set('queue.connections.rabbitmq-with-options', [ + 'driver' => 'rabbitmq', + 'queue' => 'order', + 'connection' => AMQPLazyConnection::class, + + 'hosts' => [ + [ + 'host' => getenv('HOST'), + 'port' => getenv('PORT'), + 'vhost' => '/', + 'user' => 'guest', + 'password' => 'guest', + ], + ], + + 'options' => [ + 'ssl_options' => [ + 'cafile' => null, + 'local_cert' => null, + 'local_key' => null, + 'verify_peer' => true, + 'passphrase' => null, + ], + + 'queue' => [ + 'prioritize_delayed' => true, + 'queue_max_priority' => 20, + 'exchange' => 'application-x', + 'exchange_type' => 'topic', + 'exchange_routing_key' => 'process.%s', + 'reroute_failed' => true, + 'failed_exchange' => 'failed-exchange', + 'failed_routing_key' => 'application-x.%s.failed', + ], + ], + + 'worker' => 'default', + + ]); + $app['config']->set('queue.connections.rabbitmq-with-options-empty', [ + 'driver' => 'rabbitmq', + 'queue' => 'order', + 'connection' => AMQPLazyConnection::class, + + 'hosts' => [ + [ + 'host' => getenv('HOST'), + 'port' => getenv('PORT'), + 'vhost' => '/', + 'user' => 'guest', + 'password' => 'guest', + ], + ], + + 'options' => [ + 'ssl_options' => [ + 'cafile' => null, + 'local_cert' => null, + 'local_key' => null, + 'verify_peer' => true, + 'passphrase' => null, + ], + + 'queue' => [ + 'prioritize_delayed' => '', + 'queue_max_priority' => '', + 'exchange' => '', + 'exchange_type' => '', + 'exchange_routing_key' => '', + 'reroute_failed' => '', + 'failed_exchange' => '', + 'failed_routing_key' => '', + ], + ], + + 'worker' => 'default', + + ]); + } + + /** + * @param $object + * @param string $method + * @param array $parameters + * @return mixed + * @throws \Exception + */ + protected function callMethod($object, string $method, array $parameters = []) + { + try { + $className = get_class($object); + $reflection = new \ReflectionClass($className); + } catch (\ReflectionException $e) { + throw new \Exception($e->getMessage()); + } + + $method = $reflection->getMethod($method); + $method->setAccessible(true); + + return $method->invokeArgs($object, $parameters); + } +} diff --git a/tests/TestCase.php b/tests/TestCase.php index 5eea441e..7d50fa67 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -50,8 +50,8 @@ protected function getEnvironmentSetUp($app): void ]); } - protected function connection(): RabbitMQQueue + protected function connection(string $name = null): RabbitMQQueue { - return Queue::connection(); + return Queue::connection($name); } } From 1ff32d969ab44d68ba9031d891e9b9d376d0c953 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Tue, 11 Feb 2020 14:16:02 +0300 Subject: [PATCH 137/375] add unreleased link to changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 30bf8561..d456eddd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ All notable changes to this project will be documented in this file. +## [Unreleased](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v10.1.3...master) + ## [10.1.3 (2020-01-12)](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v10.1.2...v10.1.3) - Fix 100% CPU usage of `rabbitmq:consume` command by adding sleep to consumer when no messages are got from the queue. From b8f499fed1e34ae2e9bd2374bb1fb70b64ddc795 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Tue, 11 Feb 2020 14:22:50 +0300 Subject: [PATCH 138/375] travis ci php 7.4 --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 960328da..c2182e89 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,7 @@ language: php php: - 7.2 - 7.3 + - 7.4 services: - docker From 4bcdcb93ee809eb81546645370bf9256817abb7d Mon Sep 17 00:00:00 2001 From: Emiel Bom <11626777+adm-bome@users.noreply.github.com> Date: Tue, 11 Feb 2020 21:54:49 +0100 Subject: [PATCH 139/375] fix for #310 (#311) --- src/Queue/RabbitMQQueue.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index 5092ac61..dacdff0e 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -518,7 +518,7 @@ protected function getRandomId(): string public function close(): void { if ($this->currentJob && ! $this->currentJob->isDeletedOrReleased()) { - $this->reject($this->currentJob); + $this->reject($this->currentJob, true); } try { From 16a4fb3d0d7c880366dbd10bf81d91ac87799a04 Mon Sep 17 00:00:00 2001 From: Emiel Bom <11626777+adm-bome@users.noreply.github.com> Date: Tue, 18 Feb 2020 13:41:48 +0100 Subject: [PATCH 140/375] fix unnecessary line of code (#312) - after the merge of pull-request #304, this line can be removed. --- src/Queue/Jobs/RabbitMQJob.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Queue/Jobs/RabbitMQJob.php b/src/Queue/Jobs/RabbitMQJob.php index 7f7af1d7..b20f22e9 100644 --- a/src/Queue/Jobs/RabbitMQJob.php +++ b/src/Queue/Jobs/RabbitMQJob.php @@ -81,7 +81,6 @@ public function attempts(): int $data = $headers->getNativeData(); $laravelAttempts = (int) Arr::get($data, 'laravel.attempts', 0); - $xDeathCount = (int) Arr::get($headers->getNativeData(), 'x-death.0.count', 0); return ($laravelAttempts) + 1; } From 7b48c32396ce37da96141abe9750d015de83fa0b Mon Sep 17 00:00:00 2001 From: Emiel Bom <11626777+adm-bome@users.noreply.github.com> Date: Sun, 23 Feb 2020 23:38:59 +0100 Subject: [PATCH 141/375] fixed unacked message when class not found. (#314) --- src/Queue/Jobs/RabbitMQJob.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Queue/Jobs/RabbitMQJob.php b/src/Queue/Jobs/RabbitMQJob.php index b20f22e9..279d8fc3 100644 --- a/src/Queue/Jobs/RabbitMQJob.php +++ b/src/Queue/Jobs/RabbitMQJob.php @@ -85,14 +85,17 @@ public function attempts(): int return ($laravelAttempts) + 1; } + /** + * @param null $e + */ public function fail($e = null): void { - parent::fail($e); - // We must tel rabbitMQ this Job is failed // The message must be rejected when the Job marked as failed, in case rabbitMQ wants to do some extra magic. // like: Death lettering the message to an other exchange/routing-key. $this->rabbitmq->reject($this); + + parent::fail($e); } /** From 2bd1cab40274ca42a9bf1b9d59645ccd6714d5a3 Mon Sep 17 00:00:00 2001 From: Shamsudin Date: Wed, 25 Mar 2020 01:32:28 +0300 Subject: [PATCH 142/375] Update composer.json (#319) --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 3c131f16..150e4ca8 100644 --- a/composer.json +++ b/composer.json @@ -11,7 +11,7 @@ "require": { "php": "^7.2", "ext-json": "*", - "illuminate/queue": "^6.0", + "illuminate/queue": "^6.0|^7.0", "php-amqplib/php-amqplib": "^2.11" }, "require-dev": { From 546d69b5808144abb098e6c7663a6281f22f05ac Mon Sep 17 00:00:00 2001 From: Emiel Bom <11626777+adm-bome@users.noreply.github.com> Date: Sun, 29 Mar 2020 20:40:37 +0200 Subject: [PATCH 143/375] code clean-up (#317) * Better place to tell rabbitMQ Job has failed. * cleanup * cleanup style (not showing-up in local style test) --- src/Console/ExchangeDeleteCommand.php | 41 +++++ src/Console/QueueDeleteCommand.php | 43 +++++ src/Queue/Jobs/RabbitMQJob.php | 46 ++++-- src/Queue/RabbitMQQueue.php | 216 +++++++++++++++++++------ tests/Feature/TestCase.php | 26 ++- tests/Functional/RabbitMQQueueTest.php | 97 ++++++++++- 6 files changed, 392 insertions(+), 77 deletions(-) create mode 100644 src/Console/ExchangeDeleteCommand.php create mode 100644 src/Console/QueueDeleteCommand.php diff --git a/src/Console/ExchangeDeleteCommand.php b/src/Console/ExchangeDeleteCommand.php new file mode 100644 index 00000000..af4473a2 --- /dev/null +++ b/src/Console/ExchangeDeleteCommand.php @@ -0,0 +1,41 @@ +laravel['config']->get('queue.connections.'.$this->argument('connection')); + + $queue = $connector->connect($config); + + if (! $queue->isExchangeExists($this->argument('name'))) { + $this->warn('Exchange does not exist.'); + + return; + } + + $queue->deleteExchange( + $this->argument('name'), + (bool) $this->option('unused') + ); + + $this->info('Exchange deleted successfully.'); + } +} diff --git a/src/Console/QueueDeleteCommand.php b/src/Console/QueueDeleteCommand.php new file mode 100644 index 00000000..b2eba2cc --- /dev/null +++ b/src/Console/QueueDeleteCommand.php @@ -0,0 +1,43 @@ +laravel['config']->get('queue.connections.'.$this->argument('connection')); + + $queue = $connector->connect($config); + + if (! $queue->isQueueExists($this->argument('name'))) { + $this->warn('Queue does not exist.'); + + return; + } + + $queue->deleteQueue( + $this->argument('name'), + (bool) $this->option('unused'), + (bool) $this->option('empty') + ); + + $this->info('Queue deleted successfully.'); + } +} diff --git a/src/Queue/Jobs/RabbitMQJob.php b/src/Queue/Jobs/RabbitMQJob.php index 279d8fc3..70e9e7f5 100644 --- a/src/Queue/Jobs/RabbitMQJob.php +++ b/src/Queue/Jobs/RabbitMQJob.php @@ -7,6 +7,7 @@ use Illuminate\Contracts\Queue\Job as JobContract; use Illuminate\Queue\Jobs\Job; use Illuminate\Support\Arr; +use PhpAmqpLib\Exception\AMQPProtocolChannelException; use PhpAmqpLib\Message\AMQPMessage; use PhpAmqpLib\Wire\AMQPTable; use VladimirYuldashev\LaravelQueueRabbitMQ\Horizon\RabbitMQQueue as HorizonRabbitMQQueue; @@ -55,7 +56,7 @@ public function __construct( */ public function getJobId() { - return json_decode($this->message->getBody(), true)['id'] ?? null; + return $this->decoded['id'] ?? null; } /** @@ -71,31 +72,26 @@ public function getRawBody(): string */ public function attempts(): int { - /** @var AMQPTable|null $headers */ - $headers = Arr::get($this->message->get_properties(), 'application_headers'); - - if (! $headers) { + if (! $data = $this->getRabbitMQMessageHeaders()) { return 1; } - $data = $headers->getNativeData(); - $laravelAttempts = (int) Arr::get($data, 'laravel.attempts', 0); - return ($laravelAttempts) + 1; + return $laravelAttempts + 1; } /** - * @param null $e + * {@inheritdoc} */ - public function fail($e = null): void + public function markAsFailed(): void { + parent::markAsFailed(); + // We must tel rabbitMQ this Job is failed // The message must be rejected when the Job marked as failed, in case rabbitMQ wants to do some extra magic. // like: Death lettering the message to an other exchange/routing-key. $this->rabbitmq->reject($this); - - parent::fail($e); } /** @@ -120,18 +116,21 @@ public function delete(): void } /** - * {@inheritdoc} + * Release the job back into the queue. + * + * @param int $delay + * @throws AMQPProtocolChannelException */ public function release($delay = 0): void { parent::release(); // Always create a new message when this Job is released - $this->rabbitmq->laterRaw($delay, $this->message->body, $this->queue, $this->attempts()); + $this->rabbitmq->laterRaw($delay, $this->message->getBody(), $this->queue, $this->attempts()); // Releasing a Job means the message was failed to process. - // Because this Job is always recreated and pushed as new message, this Job is correctly handled. - // We must tell rabbitMQ this fact. + // Because this Job message is always recreated and pushed as new message, this Job message is correctly handled. + // We must tell rabbitMQ this job message can be removed by acknowledging the message. $this->rabbitmq->ack($this); } @@ -154,4 +153,19 @@ public function getRabbitMQMessage(): AMQPMessage { return $this->message; } + + /** + * Get the headers from the rabbitMQ message. + * + * @return array|null + */ + protected function getRabbitMQMessageHeaders(): ?array + { + /** @var AMQPTable|null $headers */ + if (! $headers = Arr::get($this->message->get_properties(), 'application_headers')) { + return null; + } + + return $headers->getNativeData(); + } } diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index dacdff0e..b2fc05db 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -130,25 +130,13 @@ public function push($job, $data = '', $queue = null) */ public function pushRaw($payload, $queue = null, array $options = []) { - $destination = $this->getQueue($queue); - $exchange = $this->getExchange(); + [$destination, $exchange, $exchangeType, $attempts] = $this->publishProperties($queue, $options); - // When a exchange is defined and no exchange is present in RabbitMQ, create an exchange. - if ($exchange && ! $this->isExchangeExists($exchange)) { - $this->declareExchange($exchange, $this->getExchangeType()); - } + $this->declareDestination($destination, $exchange, $exchangeType); - // When no exchange is defined, create a queue for amq.direct publishing, but only if it not already present. - if (! $exchange && ! $this->isQueueExists($destination)) { - $this->declareQueue($destination, true, false, $this->getQueueArguments($destination)); - } - - [$message, $correlationId] = ($attempts = Arr::get($options, 'attempts')) - ? $this->createMessage($payload, $attempts) - : $this->createMessage($payload); + [$message, $correlationId] = $this->createMessage($payload, $attempts); - // Publish the message - $this->channel->basic_publish($message, $exchange, $this->getRoutingKey($destination), true, false); + $this->channel->basic_publish($message, $exchange, $destination, true, false); return $correlationId; } @@ -173,7 +161,6 @@ public function later($delay, $job, $data = '', $queue = null) * @param null $queue * @param int $attempts * @return mixed - * * @throws AMQPProtocolChannelException */ public function laterRaw($delay, $payload, $queue = null, $attempts = 0) @@ -204,28 +191,31 @@ public function laterRaw($delay, $payload, $queue = null, $attempts = 0) */ public function bulk($jobs, $data = '', $queue = null): void { - $destination = $this->getQueue($queue); - $exchange = $this->getExchange(); - - // When a exchange is defined and no exchange is present in RabbitMQ, create an exchange. - if ($exchange && ! $this->isExchangeExists($exchange)) { - $this->declareExchange($exchange, $this->getExchangeType()); + foreach ((array) $jobs as $job) { + $this->bulkRaw($this->createPayload($job, $queue, $data), $queue, ['job' => $job]); } - // When no exchange is defined, create a queue for amq.direct publishing, but only if it not already present. - if (! $exchange && ! $this->isQueueExists($destination)) { - $this->declareQueue($destination, true, false, $this->getQueueArguments($destination)); - } + $this->channel->publish_batch(); + } - foreach ((array) $jobs as $job) { - [$message] = $this->createMessage( - $this->createPayload($job, $queue, $data) - ); + /** + * @param string $payload + * @param null $queue + * @param array $options + * @return mixed + * @throws AMQPProtocolChannelException + */ + public function bulkRaw(string $payload, $queue = null, array $options = []) + { + [$destination, $exchange, $exchangeType, $attempts] = $this->publishProperties($queue, $options); - $this->channel->batch_basic_publish($message, $exchange, $destination); - } + $this->declareDestination($destination, $exchange, $exchangeType); - $this->channel->publish_batch(); + [$message, $correlationId] = $this->createMessage($payload, $attempts); + + $this->channel->batch_basic_publish($message, $exchange, $destination); + + return $correlationId; } /** @@ -284,6 +274,7 @@ public function getChannel(): AMQPChannel /** * Gets a queue/destination, by default the queue option set on the connection. + * * @param null $queue * @return string */ @@ -319,22 +310,18 @@ public function isExchangeExists(string $exchange): bool } /** - * Declare a exchange in rabbitMQ. + * Declare a exchange in rabbitMQ, when not already declared. * * @param string $name * @param string $type * @param bool $durable * @param bool $autoDelete * @param array $arguments + * @return void */ - public function declareExchange( - string $name, - string $type = AMQPExchangeType::DIRECT, - bool $durable = true, - bool $autoDelete = false, - array $arguments = [] - ): void { - if (in_array($name, $this->exchanges, true)) { + public function declareExchange(string $name, string $type = AMQPExchangeType::DIRECT, bool $durable = true, bool $autoDelete = false, array $arguments = []): void + { + if ($this->isExchangeDeclared($name)) { return; } @@ -350,6 +337,26 @@ public function declareExchange( ); } + /** + * Delete a exchange from rabbitMQ, only when present in RabbitMQ. + * + * @param string $name + * @param bool $unused + * @return void + * @throws AMQPProtocolChannelException + */ + public function deleteExchange(string $name, bool $unused = false): void + { + if (! $this->isExchangeExists($name)) { + return; + } + + $this->channel->exchange_delete( + $name, + $unused + ); + } + /** * Checks if the given queue already present/defined in RabbitMQ. * Returns false when when the queue is missing. @@ -358,14 +365,12 @@ public function declareExchange( * @return bool * @throws AMQPProtocolChannelException */ - public function isQueueExists(?string $name = null): bool + public function isQueueExists(string $name = null): bool { try { - $name = $this->getQueue($name); - // create a temporary channel, so the main channel will not be closed on exception $channel = $this->connection->channel(); - $channel->queue_declare($name, true); + $channel->queue_declare($this->getQueue($name), true); $channel->close(); return true; @@ -379,16 +384,17 @@ public function isQueueExists(?string $name = null): bool } /** - * Declare a queue in rabbitMQ. + * Declare a queue in rabbitMQ, when not already declared. * * @param string $name * @param bool $durable * @param bool $autoDelete * @param array $arguments + * @return void */ public function declareQueue(string $name, bool $durable = true, bool $autoDelete = false, array $arguments = []): void { - if (in_array($name, $this->queues, true)) { + if ($this->isQueueDeclared($name)) { return; } @@ -404,9 +410,30 @@ public function declareQueue(string $name, bool $durable = true, bool $autoDelet } /** + * Delete a queue from rabbitMQ, only when present in RabbitMQ. + * + * @param string $name + * @param bool $if_unused + * @param bool $if_empty + * @return void + * @throws AMQPProtocolChannelException + */ + public function deleteQueue(string $name, bool $if_unused = false, bool $if_empty = false): void + { + if (! $this->isQueueExists($name)) { + return; + } + + $this->channel->queue_delete($name, $if_unused, $if_empty); + } + + /** + * Bind a queue to an exchange. + * * @param string $queue * @param string $exchange * @param string $routingKey + * @return void */ public function bindQueue(string $queue, string $exchange, string $routingKey = ''): void { @@ -422,9 +449,12 @@ public function bindQueue(string $queue, string $exchange, string $routingKey = } /** - * @param null $queue + * Purge the queue of messages. + * + * @param string $queue + * @return void */ - public function purge($queue = null): void + public function purge(string $queue = null): void { // create a temporary channel, so the main channel will not be closed on exception $channel = $this->connection->channel(); @@ -433,7 +463,10 @@ public function purge($queue = null): void } /** + * Acknowledge the message. + * * @param RabbitMQJob $job + * @return void */ public function ack(RabbitMQJob $job): void { @@ -441,10 +474,12 @@ public function ack(RabbitMQJob $job): void } /** - * Reject current Job. + * Reject the message. * * @param RabbitMQJob $job * @param bool $requeue + * + * @return void */ public function reject(RabbitMQJob $job, bool $requeue = false): void { @@ -513,6 +548,9 @@ protected function getRandomId(): string } /** + * Close the connection to RabbitMQ. + * + * @return void * @throws Exception */ public function close(): void @@ -531,6 +569,7 @@ public function close(): void /** * Get the Queue arguments. * + * @param string $destination * @return array */ protected function getQueueArguments(string $destination): array @@ -621,7 +660,7 @@ protected function getRoutingKey(string $destination): string * @param string|null $type * @return string */ - protected function getExchangeType(string $type = null): string + protected function getExchangeType(?string $type = null): string { return @constant(AMQPExchangeType::class.'::'.Str::upper($type ?: Arr::get($this->options, 'exchange_type') ?: 'direct')) ?: AMQPExchangeType::DIRECT; } @@ -658,4 +697,75 @@ protected function getFailedRoutingKey(string $destination): string { return ltrim(sprintf(Arr::get($this->options, 'failed_routing_key') ?: '%s.failed', $destination), '.'); } + + /** + * Checks if the exchange was already declared. + * + * @param string $name + * @return bool + */ + protected function isExchangeDeclared(string $name): bool + { + return in_array($name, $this->exchanges, true); + } + + /** + * Checks if the queue was already declared. + * + * @param string $name + * @return bool + */ + protected function isQueueDeclared(string $name): bool + { + return in_array($name, $this->queues, true); + } + + /** + * Declare the destination when necessary. + * + * @param string $destination + * @param string|null $exchange + * @param string|null $exchangeType + * @return void + * @throws AMQPProtocolChannelException + */ + protected function declareDestination(string $destination, ?string $exchange = null, string $exchangeType = AMQPExchangeType::DIRECT): void + { + // When a exchange is provided and no exchange is present in RabbitMQ, create an exchange. + if ($exchange && ! $this->isExchangeExists($exchange)) { + $this->declareExchange($exchange, $exchangeType); + } + + // When a exchange is provided, just return. + if ($exchange) { + return; + } + + // When the queue already exists, just return. + if ($this->isQueueExists($destination)) { + return; + } + + // Create a queue for amq.direct publishing. + $this->declareQueue($destination, true, false, $this->getQueueArguments($destination)); + } + + /** + * Determine all publish properties. + * + * @param $queue + * @param array $options + * @return array + */ + protected function publishProperties($queue, array $options = []): array + { + $queue = $this->getQueue($queue); + $attempts = Arr::get($options, 'attempts') ?: 0; + + $destination = $this->getRoutingKey($queue); + $exchange = $this->getExchange(); + $exchangeType = $this->getExchangeType(); + + return [$destination, $exchange, $exchangeType, $attempts]; + } } diff --git a/tests/Feature/TestCase.php b/tests/Feature/TestCase.php index 3f7092ed..da3cd459 100644 --- a/tests/Feature/TestCase.php +++ b/tests/Feature/TestCase.php @@ -14,7 +14,7 @@ abstract class TestCase extends BaseTestCase /** * @throws AMQPProtocolChannelException */ - protected function setUp(): void + public function setUp(): void { parent::setUp(); @@ -62,7 +62,6 @@ public function testPushRaw(): void $this->assertNull($job->getJobId()); $job->delete(); - $this->assertSame(0, Queue::size()); } @@ -88,6 +87,9 @@ public function testPush(): void $this->assertNull($payload['timeout']); $this->assertNull($payload['timeoutAt']); $this->assertSame($job->getJobId(), $payload['id']); + + $job->delete(); + $this->assertSame(0, Queue::size()); } public function testLaterRaw(): void @@ -117,7 +119,6 @@ public function testLaterRaw(): void $this->assertSame($data, $body['data']); $job->delete(); - $this->assertSame(0, Queue::size()); } @@ -145,7 +146,6 @@ public function testLater(): void $this->assertNotNull($job->getJobId()); $job->delete(); - $this->assertSame(0, Queue::size()); } @@ -186,6 +186,9 @@ public function testReleaseRaw(): void $this->assertSame($attempt, $job->attempts()); } + + $job->delete(); + $this->assertSame(0, Queue::size()); } public function testRelease(): void @@ -209,6 +212,9 @@ public function testRelease(): void $this->assertSame($attempt, $job->attempts()); } + + $job->delete(); + $this->assertSame(0, Queue::size()); } public function testReleaseWithDelayRaw(): void @@ -237,6 +243,9 @@ public function testReleaseWithDelayRaw(): void $this->assertSame($attempt, $job->attempts()); } + + $job->delete(); + $this->assertSame(0, Queue::size()); } public function testReleaseInThePast(): void @@ -248,7 +257,10 @@ public function testReleaseInThePast(): void sleep(1); - $this->assertInstanceOf(RabbitMQJob::class, Queue::pop()); + $this->assertInstanceOf(RabbitMQJob::class, $job = Queue::pop()); + + $job->delete(); + $this->assertSame(0, Queue::size()); } public function testReleaseAndReleaseWithDelayAttempts(): void @@ -272,8 +284,10 @@ public function testReleaseAndReleaseWithDelayAttempts(): void sleep(4); $this->assertNotNull($job = Queue::pop()); - $this->assertSame(3, $job->attempts()); + + $job->delete(); + $this->assertSame(0, Queue::size()); } public function testDelete(): void diff --git a/tests/Functional/RabbitMQQueueTest.php b/tests/Functional/RabbitMQQueueTest.php index 4781e19d..086f116e 100644 --- a/tests/Functional/RabbitMQQueueTest.php +++ b/tests/Functional/RabbitMQQueueTest.php @@ -2,6 +2,7 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Functional; +use Illuminate\Support\Str; use PhpAmqpLib\Exchange\AMQPExchangeType; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue; use VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Functional\TestCase as BaseTestCase; @@ -139,13 +140,105 @@ public function testFailedRoutingKey(): void $this->assertSame('test.failed', $this->callMethod($queue, 'getFailedRoutingKey', ['test'])); } + public function testDeclareDeleteExchange(): void + { + /** @var $queue RabbitMQQueue */ + $queue = $this->connection(); + + $name = Str::random(); + + $this->assertFalse($queue->isExchangeExists($name)); + + $queue->declareExchange($name); + $this->assertTrue($queue->isExchangeExists($name)); + + $queue->deleteExchange($name); + $this->assertFalse($queue->isExchangeExists($name)); + } + + public function testDeclareDeleteQueue(): void + { + /** @var $queue RabbitMQQueue */ + $queue = $this->connection(); + + $name = Str::random(); + + $this->assertFalse($queue->isQueueExists($name)); + + $queue->declareQueue($name); + $this->assertTrue($queue->isQueueExists($name)); + + $queue->deleteQueue($name); + $this->assertFalse($queue->isQueueExists($name)); + } + public function testQueueArguments(): void { - $this->assertTrue(true); + $name = Str::random(); + + /** @var $queue RabbitMQQueue */ + $queue = $this->connection(); + $actual = $this->callMethod($queue, 'getQueueArguments', [$name]); + $expected = []; + $this->assertEquals(array_keys($expected), array_keys($actual)); + $this->assertEquals(array_values($expected), array_values($actual)); + + $queue = $this->connection('rabbitmq-with-options'); + $actual = $this->callMethod($queue, 'getQueueArguments', [$name]); + $expected = [ + 'x-max-priority' => 20, + 'x-dead-letter-exchange' => 'failed-exchange', + 'x-dead-letter-routing-key' => sprintf('application-x.%s.failed', $name), + ]; + + $this->assertEquals(array_keys($expected), array_keys($actual)); + $this->assertEquals(array_values($expected), array_values($actual)); + + $queue = $this->connection('rabbitmq-with-options-empty'); + $actual = $this->callMethod($queue, 'getQueueArguments', [$name]); + $expected = []; + + $this->assertEquals(array_keys($expected), array_keys($actual)); + $this->assertEquals(array_values($expected), array_values($actual)); } public function testDelayQueueArguments(): void { - $this->assertTrue(true); + $name = Str::random(); + $ttl = 12000; + + /** @var $queue RabbitMQQueue */ + $queue = $this->connection(); + $actual = $this->callMethod($queue, 'getDelayQueueArguments', [$name, $ttl]); + $expected = [ + 'x-dead-letter-exchange' => '', + 'x-dead-letter-routing-key' => $name, + 'x-message-ttl' => $ttl, + 'x-expires' => $ttl * 2, + ]; + $this->assertEquals(array_keys($expected), array_keys($actual)); + $this->assertEquals(array_values($expected), array_values($actual)); + + $queue = $this->connection('rabbitmq-with-options'); + $actual = $this->callMethod($queue, 'getDelayQueueArguments', [$name, $ttl]); + $expected = [ + 'x-dead-letter-exchange' => 'application-x', + 'x-dead-letter-routing-key' => sprintf('process.%s', $name), + 'x-message-ttl' => $ttl, + 'x-expires' => $ttl * 2, + ]; + $this->assertEquals(array_keys($expected), array_keys($actual)); + $this->assertEquals(array_values($expected), array_values($actual)); + + $queue = $this->connection('rabbitmq-with-options-empty'); + $actual = $this->callMethod($queue, 'getDelayQueueArguments', [$name, $ttl]); + $expected = [ + 'x-dead-letter-exchange' => '', + 'x-dead-letter-routing-key' => $name, + 'x-message-ttl' => $ttl, + 'x-expires' => $ttl * 2, + ]; + $this->assertEquals(array_keys($expected), array_keys($actual)); + $this->assertEquals(array_values($expected), array_values($actual)); } } From fee74daa6fa54b5b58eca9cdc8748c000c257859 Mon Sep 17 00:00:00 2001 From: vyuldashev Date: Fri, 24 Apr 2020 20:54:45 +0200 Subject: [PATCH 144/375] Arr::add for heartbeat instead of Arr::set --- src/Queue/Connectors/RabbitMQConnector.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Queue/Connectors/RabbitMQConnector.php b/src/Queue/Connectors/RabbitMQConnector.php index a3b44b98..49528320 100644 --- a/src/Queue/Connectors/RabbitMQConnector.php +++ b/src/Queue/Connectors/RabbitMQConnector.php @@ -73,7 +73,7 @@ protected function createConnection(array $config): AbstractConnection $connection = Arr::get($config, 'connection', AMQPLazyConnection::class); // manually disable heartbeat so long-running tasks will not fail - Arr::set($config, 'options.heartbeat', 0); + Arr::add($config, 'options.heartbeat', 0); return $connection::create_connection( Arr::shuffle(Arr::get($config, 'hosts', [])), From 4f06d8e1c8ff6b6f49dd94f38baaf60679d7d704 Mon Sep 17 00:00:00 2001 From: vyuldashev Date: Fri, 24 Apr 2020 21:07:06 +0200 Subject: [PATCH 145/375] changelog for 10.2.0 --- CHANGELOG.md | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d456eddd..c245e2c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,22 @@ All notable changes to this project will be documented in this file. -## [Unreleased](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v10.1.3...master) +## [Unreleased](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v10.2.0...master) + +## [10.2.0 (2020-04-24)](https://github.com/vyuldashev/la + +Huge thanks to [adm-bone](https://github.com/adm-bome) for this release. + +- Added support for Laravel 7.0 [#319](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/319) +- Added `rabbitmq:exchange-delete` artisan command [#317](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/317) +- Added `rabbitmq:queue-delete` artisan command [#317](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/317) +- Failed jobs can be rerouted to another exchange [#317](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/317) +- Exchange type is configurable [#317](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/317) +- Job attempts are fixed [#304](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/304) +- Added prioritization for failed jobs [#304](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/304) +- Fixed: if delay is not set when releasing a job, job will be lost [#304](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/304) +- Fix loosing messages when forced to close connection [#311](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/311) +- Fixed unacked message when class not found [#314](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/314) ## [10.1.3 (2020-01-12)](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v10.1.2...v10.1.3) From d8fdfc1e0ced85f64e458978ec6463858952839a Mon Sep 17 00:00:00 2001 From: Panayiotis Halouvas Date: Mon, 11 May 2020 02:36:30 +0300 Subject: [PATCH 146/375] When a job fails it tries to reject it twice. (#322) --- src/Consumer.php | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/src/Consumer.php b/src/Consumer.php index 286aa429..a7ce37ca 100644 --- a/src/Consumer.php +++ b/src/Consumer.php @@ -153,23 +153,6 @@ protected function daemonShouldRun(WorkerOptions $options, $connectionName, $que return ! ((($this->isDownForMaintenance)() && ! $options->force) || $this->paused); } - /** - * Mark the given job as failed if it has exceeded the maximum allowed attempts. - * - * @param string $connectionName - * @param Job|RabbitMQJob $job - * @param int $maxTries - * @param Exception $e - */ - protected function markJobAsFailedIfWillExceedMaxAttempts($connectionName, $job, $maxTries, $e): void - { - parent::markJobAsFailedIfWillExceedMaxAttempts($connectionName, $job, $maxTries, $e); - - if (! $job->isDeletedOrReleased()) { - $job->getRabbitMQ()->reject($job); - } - } - /** * Stop listening and bail out of the script. * From 1c50c21ce8ad227f41217466b9c32e3332f113b1 Mon Sep 17 00:00:00 2001 From: vyuldashev Date: Mon, 11 May 2020 01:39:26 +0200 Subject: [PATCH 147/375] update changelog --- CHANGELOG.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c245e2c7..04df678c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,13 @@ All notable changes to this project will be documented in this file. -## [Unreleased](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v10.2.0...master) +## [Unreleased](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v10.2.1...master) -## [10.2.0 (2020-04-24)](https://github.com/vyuldashev/la +## [10.2.1 (2020-05-11)](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v10.2.0...v10.2.1) + +- Fix: When a job fails it tries to reject it twice [#322](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/322) + +## [10.2.0 (2020-04-24)](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v10.1.3...v10.2.0) Huge thanks to [adm-bone](https://github.com/adm-bome) for this release. From 3217d8de83716eeb4eff3d2ffcc260f864a6d14c Mon Sep 17 00:00:00 2001 From: Emiel Bom <11626777+adm-bome@users.noreply.github.com> Date: Sun, 31 May 2020 14:24:35 +0200 Subject: [PATCH 148/375] Update bug-repoert.md (#336) Guidelines somewhat more strict. To indicate the issuer should do some research ;) --- .github/ISSUE_TEMPLATE/bug_report.md | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index dea6573b..d7f499d0 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -7,16 +7,30 @@ assignees: vyuldashev --- -- Laravel Version: #.#.# -- Package Version: #.#.# -- RabbitMQ Version: #.#.# +- Laravel/Lumen version: #.#.# +- RabbitMQ version: #.#.# +- Package version: #.#.# **Describe the bug** + A clear and concise description of what the bug is. **Steps To Reproduce** +What steps needed, to reproduce this bug. + +**Current behavior** + +- What happens with the worker? +- Is the message retried or put back into the queue? +- Is the message acknowledged or rejected? +- Is the message unacked? +- Is the message gone? + **Expected behavior** +What is the expected behavior + **Additional context** -Add any other context about the problem here. + +Add any other context about the problem or describe the use-case. From edf0bf7222f2e384b8c458a9b496b8d9b6e55ded Mon Sep 17 00:00:00 2001 From: Emiel Bom <11626777+adm-bome@users.noreply.github.com> Date: Thu, 4 Jun 2020 23:08:00 +0200 Subject: [PATCH 149/375] Support update (#339) * Support update * Altered according review --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7db1d753..81a90263 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ Only the latest version will get new features. Bug fixes will be provided using | 7.2 | 5.8 | August 26th, 2019 | [Documentation](https://github.com/vyuldashev/laravel-queue-rabbitmq/blob/v7.0/README.md) | | 8.0 | 5.8 | August 26th, 2019 | [Documentation](https://github.com/vyuldashev/laravel-queue-rabbitmq/blob/v8.0/README.md) | | 9 | 6 | September 3rd, 2021 | [Documentation](https://github.com/vyuldashev/laravel-queue-rabbitmq/blob/v9.0/README.md) | -| 10 | 6 | September 3rd, 2021 | [Documentation](https://github.com/vyuldashev/laravel-queue-rabbitmq/blob/master/README.md) | +| 10 | 6, 7 | September 3rd, 2021 | [Documentation](https://github.com/vyuldashev/laravel-queue-rabbitmq/blob/master/README.md) | ## Installation From d7b46d4c512c1d4edca62480f345c571297c93cd Mon Sep 17 00:00:00 2001 From: Emiel Bom <11626777+adm-bome@users.noreply.github.com> Date: Sat, 18 Jul 2020 00:03:01 +0200 Subject: [PATCH 150/375] fixed: make commands available in the app (#348) - reference #342 --- src/LaravelQueueRabbitMQServiceProvider.php | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/LaravelQueueRabbitMQServiceProvider.php b/src/LaravelQueueRabbitMQServiceProvider.php index 2cc3f235..3be0fd81 100644 --- a/src/LaravelQueueRabbitMQServiceProvider.php +++ b/src/LaravelQueueRabbitMQServiceProvider.php @@ -44,13 +44,18 @@ public function register(): void }); $this->commands([ - Console\ExchangeDeclareCommand::class, - Console\QueueBindCommand::class, - Console\QueueDeclareCommand::class, - Console\QueuePurgeCommand::class, Console\ConsumeCommand::class, ]); } + + $this->commands([ + Console\ExchangeDeclareCommand::class, + Console\ExchangeDeleteCommand::class, + Console\QueueBindCommand::class, + Console\QueueDeclareCommand::class, + Console\QueueDeleteCommand::class, + Console\QueuePurgeCommand::class, + ]); } /** From 912e6f4e71a9b083a9d680f2d033889bbdf531d1 Mon Sep 17 00:00:00 2001 From: Emiel Bom <11626777+adm-bome@users.noreply.github.com> Date: Sat, 18 Jul 2020 00:04:41 +0200 Subject: [PATCH 151/375] fixed: issue #341 (#346) --- src/Queue/Connectors/RabbitMQConnector.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Queue/Connectors/RabbitMQConnector.php b/src/Queue/Connectors/RabbitMQConnector.php index 49528320..e39f7b9a 100644 --- a/src/Queue/Connectors/RabbitMQConnector.php +++ b/src/Queue/Connectors/RabbitMQConnector.php @@ -72,8 +72,8 @@ protected function createConnection(array $config): AbstractConnection /** @var AbstractConnection $connection */ $connection = Arr::get($config, 'connection', AMQPLazyConnection::class); - // manually disable heartbeat so long-running tasks will not fail - Arr::add($config, 'options.heartbeat', 0); + // disable heartbeat when not configured, so long-running tasks will not fail + $config = Arr::add($config, 'options.heartbeat', 0); return $connection::create_connection( Arr::shuffle(Arr::get($config, 'hosts', [])), From 0ce0f95802184d30e6f3209cb0595733c3e3b88c Mon Sep 17 00:00:00 2001 From: vyuldashev Date: Sat, 18 Jul 2020 00:05:17 +0200 Subject: [PATCH 152/375] Update CHANGELOG --- CHANGELOG.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 04df678c..eeca36bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,12 @@ All notable changes to this project will be documented in this file. -## [Unreleased](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v10.2.1...master) +## [Unreleased](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v10.2.2...master) + +## [10.2.2 (2020-07-18)](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v10.2.1...v10.2.2) + +- Fix: Make Artisan commands visible not just in console [#348](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/348) +- Fix: Disable heartbeat when not configured [#346](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/346) ## [10.2.1 (2020-05-11)](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v10.2.0...v10.2.1) From f641ab110a053502dd60364e84fe6ee348df9201 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Tue, 25 Aug 2020 14:38:40 +0300 Subject: [PATCH 153/375] Fix worker getting killed by timeout when no more jobs available --- src/Consumer.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Consumer.php b/src/Consumer.php index a7ce37ca..9ed8b582 100644 --- a/src/Consumer.php +++ b/src/Consumer.php @@ -97,6 +97,11 @@ function (AMQPMessage $message) use ($connection, $options, $connectionName, $qu } $this->runJob($job, $connectionName, $options); + + + if ($this->supportsAsyncSignals()) { + $this->resetTimeoutHandler(); + } } ); From 6177be4d1a3ae0de25246a70f7aa9ed0bfc801e0 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Tue, 25 Aug 2020 17:47:27 +0300 Subject: [PATCH 154/375] Update Consumer.php Remove excess new line --- src/Consumer.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Consumer.php b/src/Consumer.php index 9ed8b582..da6e647f 100644 --- a/src/Consumer.php +++ b/src/Consumer.php @@ -97,8 +97,7 @@ function (AMQPMessage $message) use ($connection, $options, $connectionName, $qu } $this->runJob($job, $connectionName, $options); - - + if ($this->supportsAsyncSignals()) { $this->resetTimeoutHandler(); } From 12311a79cee29b15dea85df2335fce5912a95079 Mon Sep 17 00:00:00 2001 From: vyuldashev Date: Tue, 8 Sep 2020 23:56:37 +0200 Subject: [PATCH 155/375] Laravel 8 support --- .travis.yml | 1 - README.md | 3 ++- composer.json | 14 +++++++------- phpunit.xml.dist | 35 ++++++++++++++--------------------- tests/Feature/TestCase.php | 6 +++--- 5 files changed, 26 insertions(+), 33 deletions(-) diff --git a/.travis.yml b/.travis.yml index c2182e89..9526c1e7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,6 @@ language: php php: - - 7.2 - 7.3 - 7.4 diff --git a/README.md b/README.md index 81a90263..8cb7accd 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,8 @@ Only the latest version will get new features. Bug fixes will be provided using | 7.2 | 5.8 | August 26th, 2019 | [Documentation](https://github.com/vyuldashev/laravel-queue-rabbitmq/blob/v7.0/README.md) | | 8.0 | 5.8 | August 26th, 2019 | [Documentation](https://github.com/vyuldashev/laravel-queue-rabbitmq/blob/v8.0/README.md) | | 9 | 6 | September 3rd, 2021 | [Documentation](https://github.com/vyuldashev/laravel-queue-rabbitmq/blob/v9.0/README.md) | -| 10 | 6, 7 | September 3rd, 2021 | [Documentation](https://github.com/vyuldashev/laravel-queue-rabbitmq/blob/master/README.md) | +| 10 | 6, 7 | September 3rd, 2021 | [Documentation](https://github.com/vyuldashev/laravel-queue-rabbitmq/blob/v10.0/README.md) | +| 11 | 8 | March 8th, 2021 | [Documentation](https://github.com/vyuldashev/laravel-queue-rabbitmq/blob/master/README.md) | ## Installation diff --git a/composer.json b/composer.json index 150e4ca8..029e0220 100644 --- a/composer.json +++ b/composer.json @@ -9,17 +9,17 @@ } ], "require": { - "php": "^7.2", + "php": "^7.3", "ext-json": "*", - "illuminate/queue": "^6.0|^7.0", - "php-amqplib/php-amqplib": "^2.11" + "illuminate/queue": "^8.0", + "php-amqplib/php-amqplib": "^2.12" }, "require-dev": { - "phpunit/phpunit": "^8.4", + "phpunit/phpunit": "^9.3", "mockery/mockery": "^1.0", - "laravel/horizon": "^3.0", + "laravel/horizon": "^5.0", "friendsofphp/php-cs-fixer": "^2.16", - "orchestra/testbench": "^4.3" + "orchestra/testbench": "^6.0" }, "autoload": { "psr-4": { @@ -33,7 +33,7 @@ }, "extra": { "branch-alias": { - "dev-master": "10.0-dev" + "dev-master": "11.0-dev" }, "laravel": { "providers": [ diff --git a/phpunit.xml.dist b/phpunit.xml.dist index d31909dc..7e166ab8 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,17 +1,17 @@ - + + + + src/ + + + + + ./tests/ @@ -23,12 +23,5 @@ - - - src/ - - - - - + diff --git a/tests/Feature/TestCase.php b/tests/Feature/TestCase.php index da3cd459..b24f13a0 100644 --- a/tests/Feature/TestCase.php +++ b/tests/Feature/TestCase.php @@ -32,7 +32,7 @@ protected function tearDown(): void $this->connection()->purge(); } - $this->assertSame(0, Queue::size()); + self::assertSame(0, Queue::size()); parent::tearDown(); } @@ -83,9 +83,9 @@ public function testPush(): void $this->assertSame(TestJob::class, $payload['displayName']); $this->assertSame('Illuminate\Queue\CallQueuedHandler@call', $payload['job']); $this->assertNull($payload['maxTries']); - $this->assertNull($payload['delay']); + $this->assertNull($payload['backoff']); $this->assertNull($payload['timeout']); - $this->assertNull($payload['timeoutAt']); + $this->assertNull($payload['retryUntil']); $this->assertSame($job->getJobId(), $payload['id']); $job->delete(); From a2a16b23e241bbeac32fb20701d13decb6e8d8b6 Mon Sep 17 00:00:00 2001 From: vyuldashev Date: Wed, 9 Sep 2020 00:04:44 +0200 Subject: [PATCH 156/375] Laravel 8 support --- .styleci.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.styleci.yml b/.styleci.yml index bdff1b9f..fe42f6da 100644 --- a/.styleci.yml +++ b/.styleci.yml @@ -1,4 +1,2 @@ preset: laravel -disabled: - - simplified_null_return From 37dd9d430ce7c34756439f0de0fc3351e5362973 Mon Sep 17 00:00:00 2001 From: vyuldashev Date: Wed, 9 Sep 2020 00:08:33 +0200 Subject: [PATCH 157/375] changelog --- CHANGELOG.md => CHANGELOG-10x.md | 2 +- CHANGELOG-11x.md | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) rename CHANGELOG.md => CHANGELOG-10x.md (99%) create mode 100644 CHANGELOG-11x.md diff --git a/CHANGELOG.md b/CHANGELOG-10x.md similarity index 99% rename from CHANGELOG.md rename to CHANGELOG-10x.md index eeca36bd..1adfd2aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG-10x.md @@ -2,7 +2,7 @@ All notable changes to this project will be documented in this file. -## [Unreleased](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v10.2.2...master) +## [Unreleased](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v10.2.2...v10.0) ## [10.2.2 (2020-07-18)](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v10.2.1...v10.2.2) diff --git a/CHANGELOG-11x.md b/CHANGELOG-11x.md new file mode 100644 index 00000000..c02a496f --- /dev/null +++ b/CHANGELOG-11x.md @@ -0,0 +1,10 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +## [Unreleased](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v11.0.0...master) + +## [11.0.0 (2020-09-9)](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v10.2.2...v11.0.0) + +- Laravel 8 support +- Minimum PHP version is set to 7.3 From 4e24aec06371f9c34fe515e27bf64b66c3343c24 Mon Sep 17 00:00:00 2001 From: vyuldashev Date: Wed, 9 Sep 2020 00:09:20 +0200 Subject: [PATCH 158/375] changelog --- CHANGELOG.md => CHANGELOG-10x.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename CHANGELOG.md => CHANGELOG-10x.md (99%) diff --git a/CHANGELOG.md b/CHANGELOG-10x.md similarity index 99% rename from CHANGELOG.md rename to CHANGELOG-10x.md index eeca36bd..1adfd2aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG-10x.md @@ -2,7 +2,7 @@ All notable changes to this project will be documented in this file. -## [Unreleased](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v10.2.2...master) +## [Unreleased](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v10.2.2...v10.0) ## [10.2.2 (2020-07-18)](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v10.2.1...v10.2.2) From 0284d49cd083a598bee1b9ec3859fdf6a84e3967 Mon Sep 17 00:00:00 2001 From: vyuldashev Date: Wed, 9 Sep 2020 00:10:51 +0200 Subject: [PATCH 159/375] changelog --- CHANGELOG-11x.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG-11x.md b/CHANGELOG-11x.md index c02a496f..f0df54d2 100644 --- a/CHANGELOG-11x.md +++ b/CHANGELOG-11x.md @@ -4,7 +4,7 @@ All notable changes to this project will be documented in this file. ## [Unreleased](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v11.0.0...master) -## [11.0.0 (2020-09-9)](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v10.2.2...v11.0.0) +## [11.0.0 (2020-09-09)](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v10.2.2...v11.0.0) - Laravel 8 support - Minimum PHP version is set to 7.3 From f868b70ba16a960203c3db50a99595d8bc25ef46 Mon Sep 17 00:00:00 2001 From: vyuldashev Date: Sat, 19 Sep 2020 23:34:39 +0200 Subject: [PATCH 160/375] fix #362, fix #363 --- src/Console/ConsumeCommand.php | 2 ++ src/Horizon/RabbitMQQueue.php | 4 ++-- src/Queue/RabbitMQQueue.php | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Console/ConsumeCommand.php b/src/Console/ConsumeCommand.php index 554d6981..a1985672 100644 --- a/src/Console/ConsumeCommand.php +++ b/src/Console/ConsumeCommand.php @@ -10,6 +10,7 @@ class ConsumeCommand extends WorkCommand { protected $signature = 'rabbitmq:consume {connection? : The name of the queue connection to work} + {--name=default : The name of the consumer} {--queue= : The names of the queues to work} {--once : Only process the next job on the queue} {--stop-when-empty : Stop when the queue is empty} @@ -33,6 +34,7 @@ public function handle(): void $consumer = $this->worker; $consumer->setContainer($this->laravel); + $consumer->setName($this->option('name')); $consumer->setConsumerTag($this->consumerTag()); $consumer->setPrefetchSize((int) $this->option('prefetch-size')); $consumer->setPrefetchCount((int) $this->option('prefetch-count')); diff --git a/src/Horizon/RabbitMQQueue.php b/src/Horizon/RabbitMQQueue.php index 4389bd16..7ccfac36 100644 --- a/src/Horizon/RabbitMQQueue.php +++ b/src/Horizon/RabbitMQQueue.php @@ -4,10 +4,10 @@ use Illuminate\Contracts\Container\BindingResolutionException; use Illuminate\Contracts\Events\Dispatcher; +use Illuminate\Support\Str; use Laravel\Horizon\Events\JobDeleted; use Laravel\Horizon\Events\JobPushed; use Laravel\Horizon\Events\JobReserved; -use Laravel\Horizon\JobId; use Laravel\Horizon\JobPayload; use PhpAmqpLib\Exception\AMQPProtocolChannelException; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Jobs\RabbitMQJob; @@ -125,6 +125,6 @@ protected function event($queue, $event): void */ protected function getRandomId(): string { - return JobId::generate(); + return Str::uuid(); } } diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index b2fc05db..03b211e4 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -544,7 +544,7 @@ protected function createPayloadArray($job, $queue, $data = '') */ protected function getRandomId(): string { - return Str::random(32); + return Str::uuid(); } /** From 2b1097b565308e63c7c45904222793ac4fb5e1e6 Mon Sep 17 00:00:00 2001 From: vyuldashev Date: Sat, 19 Sep 2020 23:39:04 +0200 Subject: [PATCH 161/375] update changelog --- CHANGELOG-11x.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG-11x.md b/CHANGELOG-11x.md index f0df54d2..8b5afdc5 100644 --- a/CHANGELOG-11x.md +++ b/CHANGELOG-11x.md @@ -2,7 +2,12 @@ All notable changes to this project will be documented in this file. -## [Unreleased](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v11.0.0...master) +## [Unreleased](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v11.0.1...master) + +## [11.0.1 (2020-09-19)](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v11.0.0...v11.0.1) + +- Fix rabbitmq:consume name option does not exist [#363](https://github.com/vyuldashev/laravel-queue-rabbitmq/issues/363) +- Fix Class 'Laravel\Horizon\JobId' not found [#362](https://github.com/vyuldashev/laravel-queue-rabbitmq/issues/362) ## [11.0.0 (2020-09-09)](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v10.2.2...v11.0.0) From 7eade4957630770ecd5bf8ab66e725b506d6b618 Mon Sep 17 00:00:00 2001 From: vyuldashev Date: Sat, 19 Sep 2020 23:39:42 +0200 Subject: [PATCH 162/375] update gitattributes --- .gitattributes | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitattributes b/.gitattributes index ff1a3cb7..9e5277b7 100644 --- a/.gitattributes +++ b/.gitattributes @@ -8,7 +8,7 @@ .styleci.yml export-ignore .travis.yml export-ignore .php_cs.dist export-ignore -CHANGELOG.md export-ignore +CHANGELOG-* export-ignore CODE_OF_CONDUCT.md export-ignore CONTRIBUTING.md export-ignore phpunit.xml.dist export-ignore From 98d279975bd7846f906b41d4c84daa968bff3320 Mon Sep 17 00:00:00 2001 From: vyuldashev Date: Sun, 20 Sep 2020 23:48:18 +0200 Subject: [PATCH 163/375] Add missing options to rabbitmq:consume command --- CHANGELOG-11x.md | 6 +++++- src/Console/ConsumeCommand.php | 5 ++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG-11x.md b/CHANGELOG-11x.md index 8b5afdc5..066b5a52 100644 --- a/CHANGELOG-11x.md +++ b/CHANGELOG-11x.md @@ -2,7 +2,11 @@ All notable changes to this project will be documented in this file. -## [Unreleased](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v11.0.1...master) +## [Unreleased](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v11.0.2...master) + +## [11.0.2 (2020-09-20)](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v11.0.1...v11.0.2) + +- Add missing options to rabbitmq:consume command [#363](https://github.com/vyuldashev/laravel-queue-rabbitmq/issues/363) ## [11.0.1 (2020-09-19)](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v11.0.0...v11.0.1) diff --git a/src/Console/ConsumeCommand.php b/src/Console/ConsumeCommand.php index a1985672..ab843f99 100644 --- a/src/Console/ConsumeCommand.php +++ b/src/Console/ConsumeCommand.php @@ -14,7 +14,10 @@ class ConsumeCommand extends WorkCommand {--queue= : The names of the queues to work} {--once : Only process the next job on the queue} {--stop-when-empty : Stop when the queue is empty} - {--delay=0 : The number of seconds to delay failed jobs} + {--delay=0 : The number of seconds to delay failed jobs (Deprecated)} + {--backoff=0 : The number of seconds to wait before retrying a job that encountered an uncaught exception} + {--max-jobs=0 : The number of jobs to process before stopping} + {--max-time=0 : The maximum number of seconds the worker should run} {--force : Force the worker to run even in maintenance mode} {--memory=128 : The memory limit in megabytes} {--sleep=3 : Number of seconds to sleep when no job is available} From 9cc7c22cf30dd9bc653ce8a2ba55b1904aeac64c Mon Sep 17 00:00:00 2001 From: Omni Adams Date: Thu, 1 Oct 2020 15:15:33 -0500 Subject: [PATCH 164/375] Fix description of the queue-delete command (#366) Looks like it was copy/pasted from the queue-declare command. --- src/Console/QueueDeleteCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Console/QueueDeleteCommand.php b/src/Console/QueueDeleteCommand.php index b2eba2cc..a1e331e2 100644 --- a/src/Console/QueueDeleteCommand.php +++ b/src/Console/QueueDeleteCommand.php @@ -14,7 +14,7 @@ class QueueDeleteCommand extends Command {--unused=0 : Check if queue has no consumers} {--empty=0 : Check if queue is empty}'; - protected $description = 'Declare queue'; + protected $description = 'Delete queue'; /** * @param RabbitMQConnector $connector From 92302627a3c857bfaa11ccc221d804bf36d1ea12 Mon Sep 17 00:00:00 2001 From: Emiel Bom Date: Sun, 11 Oct 2020 15:24:14 +0200 Subject: [PATCH 165/375] added: possibility to add custom jobClass to the config. --- src/Consumer.php | 6 ++++-- src/Queue/RabbitMQQueue.php | 19 ++++++++++++++++++- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/Consumer.php b/src/Consumer.php index a7ce37ca..d1e0cc89 100644 --- a/src/Consumer.php +++ b/src/Consumer.php @@ -74,6 +74,8 @@ public function daemon($connectionName, $queue, WorkerOptions $options): void null ); + $jobClass = $connection->getJobClass(); + $this->channel->basic_consume( $queue, $this->consumerTag, @@ -81,10 +83,10 @@ public function daemon($connectionName, $queue, WorkerOptions $options): void false, false, false, - function (AMQPMessage $message) use ($connection, $options, $connectionName, $queue): void { + function (AMQPMessage $message) use ($connection, $options, $connectionName, $queue, $jobClass): void { $this->gotJob = true; - $job = new RabbitMQJob( + $job = new $jobClass( $this->container, $connection, $message, diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index 03b211e4..677dfef2 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -228,9 +228,11 @@ public function pop($queue = null) try { $queue = $this->getQueue($queue); + $job = $this->getJobClass(); + /** @var AMQPMessage|null $message */ if ($message = $this->channel->basic_get($queue)) { - return $this->currentJob = new RabbitMQJob( + return $this->currentJob = new $job( $this->container, $this, $message, @@ -272,6 +274,21 @@ public function getChannel(): AMQPChannel return $this->channel; } + /** + * Gets the Job class from config or returns the default job class + * when the job class does not extend the default job class an exception is thrown + * + * @return array|\ArrayAccess|mixed + * @throws \Throwable + */ + public function getJobClass() + { + $job = Arr::get($this->options, 'job', RabbitMQJob::class); + throw_if(! is_a($job, RabbitMQJob::class, true), Exception::class, sprintf('Class %s must extend: %s', $job, RabbitMQJob::class)); + + return $job; + } + /** * Gets a queue/destination, by default the queue option set on the connection. * From 87a11d9c17db49329e6084d47d3f3c7506418bca Mon Sep 17 00:00:00 2001 From: Emiel Bom Date: Sun, 11 Oct 2020 15:41:38 +0200 Subject: [PATCH 166/375] added: fixed issues --- src/Consumer.php | 8 ++++++-- src/Queue/RabbitMQQueue.php | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Consumer.php b/src/Consumer.php index d1e0cc89..7defa96d 100644 --- a/src/Consumer.php +++ b/src/Consumer.php @@ -12,7 +12,6 @@ use PhpAmqpLib\Message\AMQPMessage; use Symfony\Component\Debug\Exception\FatalThrowableError; use Throwable; -use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Jobs\RabbitMQJob; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue; class Consumer extends Worker @@ -74,7 +73,12 @@ public function daemon($connectionName, $queue, WorkerOptions $options): void null ); - $jobClass = $connection->getJobClass(); + try { + $jobClass = $connection->getJobClass(); + } catch (Throwable $exception) { + report($exception); + $this->kill(2); + } $this->channel->basic_consume( $queue, diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index 677dfef2..318a9fdb 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -276,7 +276,7 @@ public function getChannel(): AMQPChannel /** * Gets the Job class from config or returns the default job class - * when the job class does not extend the default job class an exception is thrown + * when the job class does not extend the default job class an exception is thrown. * * @return array|\ArrayAccess|mixed * @throws \Throwable From 8cb04e8fc30de7687c83e334e1b6a4e00b3471a9 Mon Sep 17 00:00:00 2001 From: Emiel Bom Date: Sun, 11 Oct 2020 17:26:33 +0200 Subject: [PATCH 167/375] added: new feature to the README.md --- README.md | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/README.md b/README.md index 8cb7accd..725b7521 100644 --- a/README.md +++ b/README.md @@ -161,7 +161,87 @@ When you want to instruct RabbitMQ to reroute failed messages to a exchange or a // ... ], ``` +### Use your own RabbitMQJob class +Sometimes you have to work with messages published by another application. +Those messages probably won't respect the laravel QueueApi payload schema. +The problem with these messages is that, laravel workers won't be able to determine the actual job or class to execute. +You can extend the build-in `RabbitMQJob::class` and within the queue connection config, you can add your own class. +When you specify an `job` key in the config, with your own class name, every message retrieved from the broker will get wrapped by your own class. + +An example for the config: +```php +'connections' => [ + // ... + + 'rabbitmq' => [ + // ... + + 'options' => [ + 'queue' => [ + // ... + + 'job' => \App\Queue\Jobs\RabbitMQJob::class, + ], + ], + ], + + // ... +], +``` +An example of your own job class: +```php +payload(); + + $class = WhatheverClassNameToExecute::class; + $method = 'handle'; + + ($this->instance = $this->resolve($class))->{$method}($this, $payload); + } +} + +``` +Or maybe you want to add extra properties to the payload. +```php +getRawBody(), true); + $decoded['job'] = $decoded['job'] ?? 'WhatheverFullyQualifiedClassNameToExecute@handle'; + + return $decoded; + } +} +``` ## Laravel Usage Once you completed the configuration you can use Laravel Queue API. If you used other queue drivers you do not need to change anything else. If you do not know how to use Queue API, please refer to the official Laravel documentation: http://laravel.com/docs/queues From f485aee6959b815cdb6a35acd98e8adf63ce277d Mon Sep 17 00:00:00 2001 From: Emiel Bom Date: Sun, 11 Oct 2020 16:35:58 +0200 Subject: [PATCH 168/375] remove try/catch --- src/Consumer.php | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/Consumer.php b/src/Consumer.php index 7defa96d..4157821b 100644 --- a/src/Consumer.php +++ b/src/Consumer.php @@ -73,12 +73,7 @@ public function daemon($connectionName, $queue, WorkerOptions $options): void null ); - try { - $jobClass = $connection->getJobClass(); - } catch (Throwable $exception) { - report($exception); - $this->kill(2); - } + $jobClass = $connection->getJobClass(); $this->channel->basic_consume( $queue, From 5cd69882f8733182dff1cfd7ebb40a669a5911f1 Mon Sep 17 00:00:00 2001 From: Emiel Bom Date: Sun, 11 Oct 2020 18:10:59 +0200 Subject: [PATCH 169/375] fixed: docblock return param --- src/Queue/RabbitMQQueue.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index 318a9fdb..3ff29304 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -278,7 +278,7 @@ public function getChannel(): AMQPChannel * Gets the Job class from config or returns the default job class * when the job class does not extend the default job class an exception is thrown. * - * @return array|\ArrayAccess|mixed + * @return string * @throws \Throwable */ public function getJobClass() From 538d06816290b0324a9129618e27a5bb140f9058 Mon Sep 17 00:00:00 2001 From: Emiel Bom Date: Mon, 12 Oct 2020 01:32:10 +0200 Subject: [PATCH 170/375] altered: nicer example --- README.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 725b7521..4c76ecf2 100644 --- a/README.md +++ b/README.md @@ -227,7 +227,6 @@ use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Jobs\RabbitMQJob as BaseJob; class RabbitMQJob extends BaseJob { - /** * Get the decoded body of the job. * @@ -235,10 +234,10 @@ class RabbitMQJob extends BaseJob */ public function payload() { - $decoded = json_decode($this->getRawBody(), true); - $decoded['job'] = $decoded['job'] ?? 'WhatheverFullyQualifiedClassNameToExecute@handle'; - - return $decoded; + return [ + 'job' => 'WhatheverFullyQualifiedClassNameToExecute@handle', + 'data' => json_decode($this->getRawBody(), true) + ]; } } ``` From 03dcf879b000b9faf738630d8fd7fbdc38633a44 Mon Sep 17 00:00:00 2001 From: Musa <40173603+voyula@users.noreply.github.com> Date: Fri, 27 Nov 2020 06:17:21 +0300 Subject: [PATCH 171/375] PHP 8 Support --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 029e0220..90e560da 100644 --- a/composer.json +++ b/composer.json @@ -9,7 +9,7 @@ } ], "require": { - "php": "^7.3", + "php": "^7.3|^8.0", "ext-json": "*", "illuminate/queue": "^8.0", "php-amqplib/php-amqplib": "^2.12" From f6cce560d5d3644d289ca6d5f29162ffabd1b0eb Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Sat, 5 Dec 2020 00:10:41 +0100 Subject: [PATCH 172/375] remove friendsofphp/php-cs-fixer --- .php_cs.dist | 57 --------------------------------------------------- composer.json | 1 - 2 files changed, 58 deletions(-) delete mode 100644 .php_cs.dist diff --git a/.php_cs.dist b/.php_cs.dist deleted file mode 100644 index 930c161e..00000000 --- a/.php_cs.dist +++ /dev/null @@ -1,57 +0,0 @@ -in(['config', 'src', 'tests']); - -return PhpCsFixer\Config::create() - ->setUsingCache(false) - ->setFinder($finder) - ->setRules([ - 'psr0' => false, - '@PSR2' => true, - 'blank_line_after_namespace' => true, - 'braces' => true, - 'class_definition' => true, - 'concat_space' => ['spacing' => 'none'], - 'elseif' => true, - 'function_declaration' => true, - 'indentation_type' => true, - 'line_ending' => true, - 'lowercase_constants' => true, - 'lowercase_keywords' => true, - 'method_argument_space' => [ - 'ensure_fully_multiline' => true, - ], - 'no_break_comment' => true, - 'no_closing_tag' => true, - 'no_spaces_after_function_name' => true, - 'no_spaces_inside_parenthesis' => true, - 'no_trailing_whitespace' => true, - 'no_trailing_whitespace_in_comment' => true, - 'single_blank_line_at_eof' => true, - 'single_class_element_per_statement' => [ - 'elements' => ['property'], - ], - 'single_import_per_statement' => true, - 'single_line_after_imports' => true, - 'switch_case_semicolon_to_colon' => true, - 'switch_case_space' => true, - 'visibility_required' => true, - 'encoding' => true, - 'full_opening_tag' => true, - 'blank_line_before_return' => true, - 'no_trailing_comma_in_singleline_array' => true, - 'trailing_comma_in_multiline_array' => true, - 'array_indentation' => true, - 'binary_operator_spaces' => [ - 'operators' => [ - '=' => 'single_space', - ], - ], - 'fully_qualified_strict_types' => true, - 'void_return' => true, - 'cast_spaces' => [ - 'space' => 'single', - ], - 'not_operator_with_successor_space' => true, - ]); diff --git a/composer.json b/composer.json index 90e560da..a176a041 100644 --- a/composer.json +++ b/composer.json @@ -18,7 +18,6 @@ "phpunit/phpunit": "^9.3", "mockery/mockery": "^1.0", "laravel/horizon": "^5.0", - "friendsofphp/php-cs-fixer": "^2.16", "orchestra/testbench": "^6.0" }, "autoload": { From 85448fcede47dbecd2c25392369c913f55d39d11 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Sat, 5 Dec 2020 00:19:09 +0100 Subject: [PATCH 173/375] add php 8.0 to travis --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 9526c1e7..fa155a9a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,7 @@ language: php php: - 7.3 - 7.4 + - 8.0 services: - docker From 1efd25bd4c667ceb69f619c3c2b33863444d59f6 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Sat, 5 Dec 2020 00:23:44 +0100 Subject: [PATCH 174/375] update changelog --- CHANGELOG-11x.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG-11x.md b/CHANGELOG-11x.md index 066b5a52..1022a570 100644 --- a/CHANGELOG-11x.md +++ b/CHANGELOG-11x.md @@ -2,7 +2,11 @@ All notable changes to this project will be documented in this file. -## [Unreleased](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v11.0.2...master) +## [Unreleased](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v11.1.0...master) + +## [11.1.0 (2020-12-05)](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v11.0.2...v11.1.0) + +- PHP 8 Support [#378](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/378) ## [11.0.2 (2020-09-20)](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v11.0.1...v11.0.2) From b180caaf1bf02262cf8d5534b7b6e2648a6415df Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Sat, 5 Dec 2020 00:36:09 +0100 Subject: [PATCH 175/375] update support policy --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 8cb7accd..c3c3537f 100644 --- a/README.md +++ b/README.md @@ -17,9 +17,9 @@ Only the latest version will get new features. Bug fixes will be provided using | 7.1 | 5.7 | March 4th, 2019 | [Documentation](https://github.com/vyuldashev/laravel-queue-rabbitmq/blob/v7.0/README.md) | | 7.2 | 5.8 | August 26th, 2019 | [Documentation](https://github.com/vyuldashev/laravel-queue-rabbitmq/blob/v7.0/README.md) | | 8.0 | 5.8 | August 26th, 2019 | [Documentation](https://github.com/vyuldashev/laravel-queue-rabbitmq/blob/v8.0/README.md) | -| 9 | 6 | September 3rd, 2021 | [Documentation](https://github.com/vyuldashev/laravel-queue-rabbitmq/blob/v9.0/README.md) | -| 10 | 6, 7 | September 3rd, 2021 | [Documentation](https://github.com/vyuldashev/laravel-queue-rabbitmq/blob/v10.0/README.md) | -| 11 | 8 | March 8th, 2021 | [Documentation](https://github.com/vyuldashev/laravel-queue-rabbitmq/blob/master/README.md) | +| 9 | 6 | October 5th, 2021 | [Documentation](https://github.com/vyuldashev/laravel-queue-rabbitmq/blob/v9.0/README.md) | +| 10 | 6, 7 | October 5th, 2021 | [Documentation](https://github.com/vyuldashev/laravel-queue-rabbitmq/blob/v10.0/README.md) | +| 11 | 8 | April 6th, 2021 | [Documentation](https://github.com/vyuldashev/laravel-queue-rabbitmq/blob/master/README.md) | ## Installation From cbdc993c063592b1566418c463cfd60825d0b2e3 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Sat, 5 Dec 2020 01:59:31 +0100 Subject: [PATCH 176/375] remove scripts related to php-cs-fixer --- composer.json | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/composer.json b/composer.json index a176a041..30627d90 100644 --- a/composer.json +++ b/composer.json @@ -45,12 +45,9 @@ }, "scripts": { "test": [ - "@test:style", "@test:unit" ], - "test:style": "@php vendor/bin/php-cs-fixer fix --config=.php_cs.dist --allow-risky=yes --dry-run --diff --verbose", - "test:unit": "@php vendor/bin/phpunit", - "fix:style": "@php vendor/bin/php-cs-fixer fix --config=.php_cs.dist --allow-risky=yes --diff --verbose" + "test:unit": "@php vendor/bin/phpunit" }, "minimum-stability": "dev", "prefer-stable": true From 2683b3f13c602dfb152c832b1562d4508fd80183 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Sat, 5 Dec 2020 03:31:12 +0100 Subject: [PATCH 177/375] fix travis config --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index fa155a9a..ab873413 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,7 @@ language: php php: - 7.3 - 7.4 - - 8.0 + - 8.0.0 services: - docker From 93deb64d88e2eee468ab43c232b0e05910d276d1 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Sat, 5 Dec 2020 20:09:34 +0100 Subject: [PATCH 178/375] Revert "remove friendsofphp/php-cs-fixer" This reverts commit f6cce560 --- .php_cs.dist | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++ composer.json | 1 + 2 files changed, 58 insertions(+) create mode 100644 .php_cs.dist diff --git a/.php_cs.dist b/.php_cs.dist new file mode 100644 index 00000000..930c161e --- /dev/null +++ b/.php_cs.dist @@ -0,0 +1,57 @@ +in(['config', 'src', 'tests']); + +return PhpCsFixer\Config::create() + ->setUsingCache(false) + ->setFinder($finder) + ->setRules([ + 'psr0' => false, + '@PSR2' => true, + 'blank_line_after_namespace' => true, + 'braces' => true, + 'class_definition' => true, + 'concat_space' => ['spacing' => 'none'], + 'elseif' => true, + 'function_declaration' => true, + 'indentation_type' => true, + 'line_ending' => true, + 'lowercase_constants' => true, + 'lowercase_keywords' => true, + 'method_argument_space' => [ + 'ensure_fully_multiline' => true, + ], + 'no_break_comment' => true, + 'no_closing_tag' => true, + 'no_spaces_after_function_name' => true, + 'no_spaces_inside_parenthesis' => true, + 'no_trailing_whitespace' => true, + 'no_trailing_whitespace_in_comment' => true, + 'single_blank_line_at_eof' => true, + 'single_class_element_per_statement' => [ + 'elements' => ['property'], + ], + 'single_import_per_statement' => true, + 'single_line_after_imports' => true, + 'switch_case_semicolon_to_colon' => true, + 'switch_case_space' => true, + 'visibility_required' => true, + 'encoding' => true, + 'full_opening_tag' => true, + 'blank_line_before_return' => true, + 'no_trailing_comma_in_singleline_array' => true, + 'trailing_comma_in_multiline_array' => true, + 'array_indentation' => true, + 'binary_operator_spaces' => [ + 'operators' => [ + '=' => 'single_space', + ], + ], + 'fully_qualified_strict_types' => true, + 'void_return' => true, + 'cast_spaces' => [ + 'space' => 'single', + ], + 'not_operator_with_successor_space' => true, + ]); diff --git a/composer.json b/composer.json index 30627d90..eaf1af5c 100644 --- a/composer.json +++ b/composer.json @@ -18,6 +18,7 @@ "phpunit/phpunit": "^9.3", "mockery/mockery": "^1.0", "laravel/horizon": "^5.0", + "friendsofphp/php-cs-fixer": "^2.16", "orchestra/testbench": "^6.0" }, "autoload": { From f682eb36240b82547a27a8cc57f05b577243809c Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Sat, 5 Dec 2020 20:09:41 +0100 Subject: [PATCH 179/375] Revert "PHP 8 Support" This reverts commit 03dcf879 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index eaf1af5c..89112f9d 100644 --- a/composer.json +++ b/composer.json @@ -9,7 +9,7 @@ } ], "require": { - "php": "^7.3|^8.0", + "php": "^7.3", "ext-json": "*", "illuminate/queue": "^8.0", "php-amqplib/php-amqplib": "^2.12" From bc52abba8a2928c13653b9e42d8e47b2c2c1aac0 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Sat, 5 Dec 2020 20:10:24 +0100 Subject: [PATCH 180/375] Revert "remove scripts related to php-cs-fixer" This reverts commit cbdc993c --- composer.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 89112f9d..029e0220 100644 --- a/composer.json +++ b/composer.json @@ -46,9 +46,12 @@ }, "scripts": { "test": [ + "@test:style", "@test:unit" ], - "test:unit": "@php vendor/bin/phpunit" + "test:style": "@php vendor/bin/php-cs-fixer fix --config=.php_cs.dist --allow-risky=yes --dry-run --diff --verbose", + "test:unit": "@php vendor/bin/phpunit", + "fix:style": "@php vendor/bin/php-cs-fixer fix --config=.php_cs.dist --allow-risky=yes --diff --verbose" }, "minimum-stability": "dev", "prefer-stable": true From 8f01172d5c1d6f5016c76fca01b701ab8a4947c1 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Sat, 5 Dec 2020 20:10:56 +0100 Subject: [PATCH 181/375] Revert "remove scripts related to php-cs-fixer" This reverts commit cbdc993c --- .travis.yml | 1 - CHANGELOG-11x.md | 6 +----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index ab873413..9526c1e7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,6 @@ language: php php: - 7.3 - 7.4 - - 8.0.0 services: - docker diff --git a/CHANGELOG-11x.md b/CHANGELOG-11x.md index 1022a570..066b5a52 100644 --- a/CHANGELOG-11x.md +++ b/CHANGELOG-11x.md @@ -2,11 +2,7 @@ All notable changes to this project will be documented in this file. -## [Unreleased](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v11.1.0...master) - -## [11.1.0 (2020-12-05)](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v11.0.2...v11.1.0) - -- PHP 8 Support [#378](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/378) +## [Unreleased](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v11.0.2...master) ## [11.0.2 (2020-09-20)](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v11.0.1...v11.0.2) From 503158a963cb30db67a5f61cd32b3e2d8048832b Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Sat, 5 Dec 2020 20:26:48 +0100 Subject: [PATCH 182/375] wip --- README.md | 18 ++++++-- config/rabbitmq.php | 3 ++ src/Horizon/RabbitMQQueue.php | 2 +- src/Queue/RabbitMQQueue.php | 82 ++++++++++++++++++++++------------- 4 files changed, 69 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index d0cc9dea..54cff59f 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,9 @@ Add connection to `config/queue.php`: 'verify_peer' => env('RABBITMQ_SSL_VERIFY_PEER', true), 'passphrase' => env('RABBITMQ_SSL_PASSPHRASE', null), ], + 'queue' => [ + 'job' => VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Jobs\RabbitMQJob::class, + ], ], /* @@ -161,15 +164,17 @@ When you want to instruct RabbitMQ to reroute failed messages to a exchange or a // ... ], ``` + ### Use your own RabbitMQJob class Sometimes you have to work with messages published by another application. -Those messages probably won't respect the laravel QueueApi payload schema. -The problem with these messages is that, laravel workers won't be able to determine the actual job or class to execute. +Those messages probably won't respect Laravel's job payload schema. +The problem with these messages is that, Laravel workers won't be able to determine the actual job or class to execute. -You can extend the build-in `RabbitMQJob::class` and within the queue connection config, you can add your own class. +You can extend the build-in `RabbitMQJob::class` and within the queue connection config, you can define your own class. When you specify an `job` key in the config, with your own class name, every message retrieved from the broker will get wrapped by your own class. An example for the config: + ```php 'connections' => [ // ... @@ -189,7 +194,9 @@ An example for the config: // ... ], ``` + An example of your own job class: + ```php env('RABBITMQ_SSL_VERIFY_PEER', true), 'passphrase' => env('RABBITMQ_SSL_PASSPHRASE', null), ], + 'queue' => [ + 'job' => VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Jobs\RabbitMQJob::class, + ], ], /* diff --git a/src/Horizon/RabbitMQQueue.php b/src/Horizon/RabbitMQQueue.php index 7ccfac36..de577897 100644 --- a/src/Horizon/RabbitMQQueue.php +++ b/src/Horizon/RabbitMQQueue.php @@ -74,7 +74,7 @@ public function later($delay, $job, $data = '', $queue = null) public function pop($queue = null) { return tap(parent::pop($queue), function ($result) use ($queue): void { - if ($result instanceof RabbitMQJob) { + if (is_a($result, RabbitMQJob::class, true)) { $this->event($this->getQueue($queue), new JobReserved($result->getRawBody())); } }); diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index 3ff29304..4c895e83 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -10,12 +10,15 @@ use Illuminate\Queue\Queue; use Illuminate\Support\Arr; use Illuminate\Support\Str; +use JsonException; use PhpAmqpLib\Channel\AMQPChannel; use PhpAmqpLib\Connection\AbstractConnection; use PhpAmqpLib\Exception\AMQPProtocolChannelException; use PhpAmqpLib\Exchange\AMQPExchangeType; use PhpAmqpLib\Message\AMQPMessage; use PhpAmqpLib\Wire\AMQPTable; +use Throwable; +use Throwabler; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Jobs\RabbitMQJob; class RabbitMQQueue extends Queue implements QueueContract @@ -101,7 +104,7 @@ public function size($queue = null): int { $queue = $this->getQueue($queue); - if (! $this->isQueueExists($queue)) { + if (!$this->isQueueExists($queue)) { return 0; } @@ -172,7 +175,7 @@ public function laterRaw($delay, $payload, $queue = null, $attempts = 0) return $this->pushRaw($payload, $queue, ['delay' => $delay, 'attempts' => $attempts]); } - $destination = $this->getQueue($queue).'.delay.'.$ttl; + $destination = $this->getQueue($queue) . '.delay.' . $ttl; $this->declareQueue($destination, true, false, $this->getDelayQueueArguments($this->getQueue($queue), $ttl)); @@ -191,7 +194,7 @@ public function laterRaw($delay, $payload, $queue = null, $attempts = 0) */ public function bulk($jobs, $data = '', $queue = null): void { - foreach ((array) $jobs as $job) { + foreach ((array)$jobs as $job) { $this->bulkRaw($this->createPayload($job, $queue, $data), $queue, ['job' => $job]); } @@ -221,7 +224,7 @@ public function bulkRaw(string $payload, $queue = null, array $options = []) /** * {@inheritdoc} * - * @throws Exception + * @throws Throwable */ public function pop($queue = null) { @@ -244,7 +247,6 @@ public function pop($queue = null) // If there is not exchange or queue AMQP will throw exception with code 404 // We need to catch it and return null if ($exception->amqp_reply_code === 404) { - // Because of the channel exception the channel was closed and removed. // We have to open a new channel. Because else the worker(s) are stuck in a loop, without processing. $this->channel = $this->connection->channel(); @@ -275,16 +277,20 @@ public function getChannel(): AMQPChannel } /** - * Gets the Job class from config or returns the default job class - * when the job class does not extend the default job class an exception is thrown. + * Job class to use. * * @return string * @throws \Throwable */ - public function getJobClass() + public function getJobClass(): string { $job = Arr::get($this->options, 'job', RabbitMQJob::class); - throw_if(! is_a($job, RabbitMQJob::class, true), Exception::class, sprintf('Class %s must extend: %s', $job, RabbitMQJob::class)); + + throw_if( + !is_a($job, RabbitMQJob::class, true), + Exception::class, + sprintf('Class %s must extend: %s', $job, RabbitMQJob::class) + ); return $job; } @@ -295,7 +301,7 @@ public function getJobClass() * @param null $queue * @return string */ - public function getQueue($queue = null) + public function getQueue($queue = null): string { return $queue ?: $this->default; } @@ -336,8 +342,13 @@ public function isExchangeExists(string $exchange): bool * @param array $arguments * @return void */ - public function declareExchange(string $name, string $type = AMQPExchangeType::DIRECT, bool $durable = true, bool $autoDelete = false, array $arguments = []): void - { + public function declareExchange( + string $name, + string $type = AMQPExchangeType::DIRECT, + bool $durable = true, + bool $autoDelete = false, + array $arguments = [] + ): void { if ($this->isExchangeDeclared($name)) { return; } @@ -364,7 +375,7 @@ public function declareExchange(string $name, string $type = AMQPExchangeType::D */ public function deleteExchange(string $name, bool $unused = false): void { - if (! $this->isExchangeExists($name)) { + if (!$this->isExchangeExists($name)) { return; } @@ -378,7 +389,7 @@ public function deleteExchange(string $name, bool $unused = false): void * Checks if the given queue already present/defined in RabbitMQ. * Returns false when when the queue is missing. * - * @param string $name + * @param string|null $name * @return bool * @throws AMQPProtocolChannelException */ @@ -409,8 +420,12 @@ public function isQueueExists(string $name = null): bool * @param array $arguments * @return void */ - public function declareQueue(string $name, bool $durable = true, bool $autoDelete = false, array $arguments = []): void - { + public function declareQueue( + string $name, + bool $durable = true, + bool $autoDelete = false, + array $arguments = [] + ): void { if ($this->isQueueDeclared($name)) { return; } @@ -437,7 +452,7 @@ public function declareQueue(string $name, bool $durable = true, bool $autoDelet */ public function deleteQueue(string $name, bool $if_unused = false, bool $if_empty = false): void { - if (! $this->isQueueExists($name)) { + if (!$this->isQueueExists($name)) { return; } @@ -468,7 +483,7 @@ public function bindQueue(string $queue, string $exchange, string $routingKey = /** * Purge the queue of messages. * - * @param string $queue + * @param string|null $queue * @return void */ public function purge(string $queue = null): void @@ -509,6 +524,7 @@ public function reject(RabbitMQJob $job, bool $requeue = false): void * @param $payload * @param int $attempts * @return array + * @throws JsonException */ protected function createMessage($payload, int $attempts = 0): array { @@ -517,7 +533,7 @@ protected function createMessage($payload, int $attempts = 0): array 'delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT, ]; - if ($correlationId = json_decode($payload, true)['id'] ?? null) { + if ($correlationId = json_decode($payload, true, 512)['id'] ?? null) { $properties['correlation_id'] = $correlationId; } @@ -542,12 +558,12 @@ protected function createMessage($payload, int $attempts = 0): array /** * Create a payload array from the given job and data. * - * @param object|string $job + * @param string|object $job * @param string $queue - * @param string $data + * @param mixed $data * @return array */ - protected function createPayloadArray($job, $queue, $data = '') + protected function createPayloadArray($job, $queue, $data = ''): array { return array_merge(parent::createPayloadArray($job, $queue, $data), [ 'id' => $this->getRandomId(), @@ -572,7 +588,7 @@ protected function getRandomId(): string */ public function close(): void { - if ($this->currentJob && ! $this->currentJob->isDeletedOrReleased()) { + if ($this->currentJob && !$this->currentJob->isDeletedOrReleased()) { $this->reject($this->currentJob, true); } @@ -632,7 +648,7 @@ protected function getDelayQueueArguments(string $destination, int $ttl): array */ protected function isPrioritizeDelayed(): bool { - return boolval(Arr::get($this->options, 'prioritize_delayed') ?: false); + return (bool)(Arr::get($this->options, 'prioritize_delayed') ?: false); } /** @@ -645,13 +661,13 @@ protected function isPrioritizeDelayed(): bool */ protected function getQueueMaxPriority(): int { - return intval(Arr::get($this->options, 'queue_max_priority') ?: 2); + return (int)(Arr::get($this->options, 'queue_max_priority') ?: 2); } /** * Get the exchange name, or &null; as default value. * - * @param string $exchange + * @param string|null $exchange * @return string|null */ protected function getExchange(string $exchange = null): ?string @@ -679,7 +695,8 @@ protected function getRoutingKey(string $destination): string */ protected function getExchangeType(?string $type = null): string { - return @constant(AMQPExchangeType::class.'::'.Str::upper($type ?: Arr::get($this->options, 'exchange_type') ?: 'direct')) ?: AMQPExchangeType::DIRECT; + return @constant(AMQPExchangeType::class . '::' . Str::upper($type ?: Arr::get($this->options, + 'exchange_type') ?: 'direct')) ?: AMQPExchangeType::DIRECT; } /** @@ -689,7 +706,7 @@ protected function getExchangeType(?string $type = null): string */ protected function isRerouteFailed(): bool { - return boolval(Arr::get($this->options, 'reroute_failed') ?: false); + return (bool)(Arr::get($this->options, 'reroute_failed') ?: false); } /** @@ -746,10 +763,13 @@ protected function isQueueDeclared(string $name): bool * @return void * @throws AMQPProtocolChannelException */ - protected function declareDestination(string $destination, ?string $exchange = null, string $exchangeType = AMQPExchangeType::DIRECT): void - { + protected function declareDestination( + string $destination, + ?string $exchange = null, + string $exchangeType = AMQPExchangeType::DIRECT + ): void { // When a exchange is provided and no exchange is present in RabbitMQ, create an exchange. - if ($exchange && ! $this->isExchangeExists($exchange)) { + if ($exchange && !$this->isExchangeExists($exchange)) { $this->declareExchange($exchange, $exchangeType); } From 077f0440f29b5f680d9de822176df576d9e77f04 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Sat, 5 Dec 2020 20:28:29 +0100 Subject: [PATCH 183/375] update changelog --- CHANGELOG-11x.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG-11x.md b/CHANGELOG-11x.md index 066b5a52..f3d43db2 100644 --- a/CHANGELOG-11x.md +++ b/CHANGELOG-11x.md @@ -2,7 +2,11 @@ All notable changes to this project will be documented in this file. -## [Unreleased](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v11.0.2...master) +## [Unreleased](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v11.1.0...master) + +## [11.1.2 (2020-12-05)](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v11.0.2...v11.1.0) + +- Custom job class [#370](https://github.com/vyuldashev/laravel-queue-rabbitmq/issues/370) ## [11.0.2 (2020-09-20)](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v11.0.1...v11.0.2) From b17f17477c441494a5f3c2a8cb5d73408d51ade7 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Sat, 5 Dec 2020 20:30:55 +0100 Subject: [PATCH 184/375] Apply fixes from StyleCI (#382) Co-authored-by: Vladimir Yuldashev --- src/Queue/RabbitMQQueue.php | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index 4c895e83..758cb6c4 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -18,7 +18,6 @@ use PhpAmqpLib\Message\AMQPMessage; use PhpAmqpLib\Wire\AMQPTable; use Throwable; -use Throwabler; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Jobs\RabbitMQJob; class RabbitMQQueue extends Queue implements QueueContract @@ -104,7 +103,7 @@ public function size($queue = null): int { $queue = $this->getQueue($queue); - if (!$this->isQueueExists($queue)) { + if (! $this->isQueueExists($queue)) { return 0; } @@ -175,7 +174,7 @@ public function laterRaw($delay, $payload, $queue = null, $attempts = 0) return $this->pushRaw($payload, $queue, ['delay' => $delay, 'attempts' => $attempts]); } - $destination = $this->getQueue($queue) . '.delay.' . $ttl; + $destination = $this->getQueue($queue).'.delay.'.$ttl; $this->declareQueue($destination, true, false, $this->getDelayQueueArguments($this->getQueue($queue), $ttl)); @@ -194,7 +193,7 @@ public function laterRaw($delay, $payload, $queue = null, $attempts = 0) */ public function bulk($jobs, $data = '', $queue = null): void { - foreach ((array)$jobs as $job) { + foreach ((array) $jobs as $job) { $this->bulkRaw($this->createPayload($job, $queue, $data), $queue, ['job' => $job]); } @@ -287,7 +286,7 @@ public function getJobClass(): string $job = Arr::get($this->options, 'job', RabbitMQJob::class); throw_if( - !is_a($job, RabbitMQJob::class, true), + ! is_a($job, RabbitMQJob::class, true), Exception::class, sprintf('Class %s must extend: %s', $job, RabbitMQJob::class) ); @@ -375,7 +374,7 @@ public function declareExchange( */ public function deleteExchange(string $name, bool $unused = false): void { - if (!$this->isExchangeExists($name)) { + if (! $this->isExchangeExists($name)) { return; } @@ -452,7 +451,7 @@ public function declareQueue( */ public function deleteQueue(string $name, bool $if_unused = false, bool $if_empty = false): void { - if (!$this->isQueueExists($name)) { + if (! $this->isQueueExists($name)) { return; } @@ -588,7 +587,7 @@ protected function getRandomId(): string */ public function close(): void { - if ($this->currentJob && !$this->currentJob->isDeletedOrReleased()) { + if ($this->currentJob && ! $this->currentJob->isDeletedOrReleased()) { $this->reject($this->currentJob, true); } @@ -648,7 +647,7 @@ protected function getDelayQueueArguments(string $destination, int $ttl): array */ protected function isPrioritizeDelayed(): bool { - return (bool)(Arr::get($this->options, 'prioritize_delayed') ?: false); + return (bool) (Arr::get($this->options, 'prioritize_delayed') ?: false); } /** @@ -661,7 +660,7 @@ protected function isPrioritizeDelayed(): bool */ protected function getQueueMaxPriority(): int { - return (int)(Arr::get($this->options, 'queue_max_priority') ?: 2); + return (int) (Arr::get($this->options, 'queue_max_priority') ?: 2); } /** @@ -695,7 +694,7 @@ protected function getRoutingKey(string $destination): string */ protected function getExchangeType(?string $type = null): string { - return @constant(AMQPExchangeType::class . '::' . Str::upper($type ?: Arr::get($this->options, + return @constant(AMQPExchangeType::class.'::'.Str::upper($type ?: Arr::get($this->options, 'exchange_type') ?: 'direct')) ?: AMQPExchangeType::DIRECT; } @@ -706,7 +705,7 @@ protected function getExchangeType(?string $type = null): string */ protected function isRerouteFailed(): bool { - return (bool)(Arr::get($this->options, 'reroute_failed') ?: false); + return (bool) (Arr::get($this->options, 'reroute_failed') ?: false); } /** @@ -769,7 +768,7 @@ protected function declareDestination( string $exchangeType = AMQPExchangeType::DIRECT ): void { // When a exchange is provided and no exchange is present in RabbitMQ, create an exchange. - if ($exchange && !$this->isExchangeExists($exchange)) { + if ($exchange && ! $this->isExchangeExists($exchange)) { $this->declareExchange($exchange, $exchangeType); } From dd66d82ce921b1f63d5ea35f7b3d49ca2598aace Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Sat, 5 Dec 2020 20:31:51 +0100 Subject: [PATCH 185/375] fix style --- src/Queue/RabbitMQQueue.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index 758cb6c4..40578a1f 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -694,8 +694,10 @@ protected function getRoutingKey(string $destination): string */ protected function getExchangeType(?string $type = null): string { - return @constant(AMQPExchangeType::class.'::'.Str::upper($type ?: Arr::get($this->options, - 'exchange_type') ?: 'direct')) ?: AMQPExchangeType::DIRECT; + return @constant(AMQPExchangeType::class.'::'.Str::upper($type ?: Arr::get( + $this->options, + 'exchange_type' + ) ?: 'direct')) ?: AMQPExchangeType::DIRECT; } /** From 4b60f6adceb8a57ee0280bb832a8bb1ee7ad36c4 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Sat, 5 Dec 2020 20:32:52 +0100 Subject: [PATCH 186/375] fix changelog --- CHANGELOG-11x.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG-11x.md b/CHANGELOG-11x.md index f3d43db2..c326eb29 100644 --- a/CHANGELOG-11x.md +++ b/CHANGELOG-11x.md @@ -4,7 +4,7 @@ All notable changes to this project will be documented in this file. ## [Unreleased](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v11.1.0...master) -## [11.1.2 (2020-12-05)](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v11.0.2...v11.1.0) +## [11.1.0 (2020-12-05)](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v11.0.2...v11.1.0) - Custom job class [#370](https://github.com/vyuldashev/laravel-queue-rabbitmq/issues/370) From a63f61ab0f7f9c95ca430997e9d2793f6acc7138 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Mon, 7 Dec 2020 01:43:22 +0100 Subject: [PATCH 187/375] update changelog --- CHANGELOG-11x.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG-11x.md b/CHANGELOG-11x.md index c326eb29..ce013a50 100644 --- a/CHANGELOG-11x.md +++ b/CHANGELOG-11x.md @@ -2,7 +2,11 @@ All notable changes to this project will be documented in this file. -## [Unreleased](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v11.1.0...master) +## [Unreleased](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v11.1.1...master) + +## [11.1.1 (2020-12-07)](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v11.1.0...v11.1.1) + +- Fix worker is stopped by timeout when no new jobs available [#352](https://github.com/vyuldashev/laravel-queue-rabbitmq/issues/352) ## [11.1.0 (2020-12-05)](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v11.0.2...v11.1.0) From a87f20b2cbd2cf6b37ec847134e801b3c861a304 Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Tue, 8 Dec 2020 23:16:50 +0100 Subject: [PATCH 188/375] Switch to Github Actions (#383) * Switch to Github Actions * Skip PHP 8 builds for now * Remove PHP 8 builds * Bump illuminate/queue * bump php cs fixer --- .gitattributes | 1 - .github/workflows/tests.yml | 49 +++++++++++++++++++++++++++++++++++++ .travis.yml | 27 -------------------- README.md | 2 +- composer.json | 2 +- 5 files changed, 51 insertions(+), 30 deletions(-) create mode 100644 .github/workflows/tests.yml delete mode 100644 .travis.yml diff --git a/.gitattributes b/.gitattributes index 9e5277b7..f9ab78ed 100644 --- a/.gitattributes +++ b/.gitattributes @@ -6,7 +6,6 @@ .gitattributes export-ignore .gitignore export-ignore .styleci.yml export-ignore -.travis.yml export-ignore .php_cs.dist export-ignore CHANGELOG-* export-ignore CODE_OF_CONDUCT.md export-ignore diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 00000000..b31d03df --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,49 @@ +name: Tests + +on: + push: + pull_request: + schedule: + - cron: '0 0 * * *' + +jobs: + tests: + runs-on: ubuntu-latest + + strategy: + fail-fast: true + matrix: + php: [7.3, 7.4] + stability: [prefer-lowest, prefer-stable] + + name: PHP ${{ matrix.php }} - ${{ matrix.stability }} + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + extensions: dom, curl, libxml, mbstring, zip + coverage: none + + - name: Set up Docker + run: | + sudo rm /usr/local/bin/docker-compose + curl -L https://github.com/docker/compose/releases/download/1.24.1/docker-compose-`uname -s`-`uname -m` > docker-compose + chmod +x docker-compose + sudo mv docker-compose /usr/local/bin + + - name: Start Docker container + run: docker-compose up -d rabbitmq + + - name: Install dependencies + run: composer update --${{ matrix.stability }} --prefer-dist --no-interaction --no-progress + + - name: Run PHP CS Fixer + run: vendor/bin/php-cs-fixer fix --config=.php_cs.dist --allow-risky=yes --dry-run --diff --verbose + + - name: Execute tests + run: vendor/bin/phpunit --verbose diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 9526c1e7..00000000 --- a/.travis.yml +++ /dev/null @@ -1,27 +0,0 @@ -language: php - -php: - - 7.3 - - 7.4 - -services: - - docker - -cache: - directories: - - $HOME/.composer/cache - -before_install: - - sudo rm /usr/local/bin/docker-compose - - curl -L https://github.com/docker/compose/releases/download/1.24.1/docker-compose-`uname -s`-`uname -m` > docker-compose - - chmod +x docker-compose - - sudo mv docker-compose /usr/local/bin - -before_script: - - docker-compose up -d rabbitmq - - travis_retry composer self-update - - travis_retry composer update --no-progress --no-interaction --prefer-dist - - sleep 10 - -script: - - composer test diff --git a/README.md b/README.md index 54cff59f..e01f5137 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ RabbitMQ Queue driver for Laravel ====================== [![Latest Stable Version](https://poser.pugx.org/vladimir-yuldashev/laravel-queue-rabbitmq/v/stable?format=flat-square)](https://packagist.org/packages/vladimir-yuldashev/laravel-queue-rabbitmq) -[![Build Status](https://img.shields.io/travis/vyuldashev/laravel-queue-rabbitmq.svg?style=flat-square)](https://travis-ci.org/vyuldashev/laravel-queue-rabbitmq) +[![Build Status](https://github.com/vyuldashev/laravel-queue-rabbitmq/workflows/Tests/badge.svg)](https://github.com/vyuldashev/laravel-queue-rabbitmq/actions) [![Total Downloads](https://poser.pugx.org/vladimir-yuldashev/laravel-queue-rabbitmq/downloads?format=flat-square)](https://packagist.org/packages/vladimir-yuldashev/laravel-queue-rabbitmq) [![StyleCI](https://styleci.io/repos/14976752/shield)](https://styleci.io/repos/14976752) [![License](https://poser.pugx.org/vladimir-yuldashev/laravel-queue-rabbitmq/license?format=flat-square)](https://packagist.org/packages/vladimir-yuldashev/laravel-queue-rabbitmq) diff --git a/composer.json b/composer.json index 029e0220..b097891f 100644 --- a/composer.json +++ b/composer.json @@ -18,7 +18,7 @@ "phpunit/phpunit": "^9.3", "mockery/mockery": "^1.0", "laravel/horizon": "^5.0", - "friendsofphp/php-cs-fixer": "^2.16", + "friendsofphp/php-cs-fixer": "^2.17", "orchestra/testbench": "^6.0" }, "autoload": { From 98704cf57c72c6df68d5f394a26c27cc7643056f Mon Sep 17 00:00:00 2001 From: Musa <40173603+voyula@users.noreply.github.com> Date: Tue, 15 Dec 2020 21:54:40 +0300 Subject: [PATCH 189/375] Create .editorconfig (#387) --- .editorconfig | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..6537ca46 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,15 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 4 +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false + +[*.{yml,yaml}] +indent_size = 2 From 7a7841cfab2e05ff2a6c30587c0bd3b4f96b6728 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Tue, 15 Dec 2020 19:56:15 +0100 Subject: [PATCH 190/375] Update README.md --- README.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/README.md b/README.md index e01f5137..3f400490 100644 --- a/README.md +++ b/README.md @@ -12,11 +12,6 @@ Only the latest version will get new features. Bug fixes will be provided using | Package Version | Laravel Version | Bug Fixes Until | | |-----------------|-----------------|---------------------|---------------------------------------------------------------------------------------------| -| 6.0 | 5.5 | August 30th, 2019 | [Documentation](https://github.com/vyuldashev/laravel-queue-rabbitmq/blob/v6.0/README.md) | -| 7.0 | 5.6 | August 7th, 2018 | [Documentation](https://github.com/vyuldashev/laravel-queue-rabbitmq/blob/v7.0/README.md) | -| 7.1 | 5.7 | March 4th, 2019 | [Documentation](https://github.com/vyuldashev/laravel-queue-rabbitmq/blob/v7.0/README.md) | -| 7.2 | 5.8 | August 26th, 2019 | [Documentation](https://github.com/vyuldashev/laravel-queue-rabbitmq/blob/v7.0/README.md) | -| 8.0 | 5.8 | August 26th, 2019 | [Documentation](https://github.com/vyuldashev/laravel-queue-rabbitmq/blob/v8.0/README.md) | | 9 | 6 | October 5th, 2021 | [Documentation](https://github.com/vyuldashev/laravel-queue-rabbitmq/blob/v9.0/README.md) | | 10 | 6, 7 | October 5th, 2021 | [Documentation](https://github.com/vyuldashev/laravel-queue-rabbitmq/blob/v10.0/README.md) | | 11 | 8 | April 6th, 2021 | [Documentation](https://github.com/vyuldashev/laravel-queue-rabbitmq/blob/master/README.md) | From ae257c535a4cf1a05c979460deb73e4ef648cd48 Mon Sep 17 00:00:00 2001 From: Sebastien Armand Date: Thu, 11 Feb 2021 15:55:52 -0800 Subject: [PATCH 191/375] Apply timeout reset fix (#355) to v10.0 branch (#404) * Fix worker getting killed by timeout when no more jobs available * Update Consumer.php Remove excess new line Co-authored-by: Dmitry --- src/Consumer.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Consumer.php b/src/Consumer.php index a7ce37ca..da6e647f 100644 --- a/src/Consumer.php +++ b/src/Consumer.php @@ -97,6 +97,10 @@ function (AMQPMessage $message) use ($connection, $options, $connectionName, $qu } $this->runJob($job, $connectionName, $options); + + if ($this->supportsAsyncSignals()) { + $this->resetTimeoutHandler(); + } } ); From d92cf4d7860dd77f89d8ffc845a3615e9a720b41 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Fri, 12 Feb 2021 00:58:37 +0100 Subject: [PATCH 192/375] update CHANGELOG-10x.md --- CHANGELOG-10x.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG-10x.md b/CHANGELOG-10x.md index 1adfd2aa..a396c6d6 100644 --- a/CHANGELOG-10x.md +++ b/CHANGELOG-10x.md @@ -2,7 +2,11 @@ All notable changes to this project will be documented in this file. -## [Unreleased](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v10.2.2...v10.0) +## [Unreleased](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v10.2.3...v10.0) + +## [10.2.3 (2021-02-12)](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v10.2.2...v10.2.3) + +- Fix Worker is getting killed by timeout when no more jobs available [#404](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/404) ## [10.2.2 (2020-07-18)](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v10.2.1...v10.2.2) From 43456f94cf97d13868ff9b57865916125d35f9f9 Mon Sep 17 00:00:00 2001 From: Stanislav Date: Sun, 14 Feb 2021 05:09:04 +0300 Subject: [PATCH 193/375] Fix readme (#402) Fix `prioritize_delayed` option name --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3f400490..8328df6f 100644 --- a/README.md +++ b/README.md @@ -91,7 +91,7 @@ When you want to prioritize messages when they were delayed, then this is possib 'queue' => [ // ... - 'prioritize_delayed_messages' => false, + 'prioritize_delayed' => false, 'queue_max_priority' => 10, ], ], From 876c1c07e9654e97dc9fb23aca4cac7b32ca58a2 Mon Sep 17 00:00:00 2001 From: Frans Saris Date: Wed, 24 Feb 2021 16:26:15 +0100 Subject: [PATCH 194/375] Update Consumer to stop daemon process when stopIfNecessary() returns exit code --- src/Consumer.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Consumer.php b/src/Consumer.php index 737f1f76..3a8619ca 100644 --- a/src/Consumer.php +++ b/src/Consumer.php @@ -139,7 +139,13 @@ function (AMQPMessage $message) use ($connection, $options, $connectionName, $qu // Finally, we will check to see if we have exceeded our memory limits or if // the queue should restart based on other indications. If so, we'll stop // this worker and let whatever is "monitoring" it restart the process. - $this->stopIfNecessary($options, $lastRestart, $this->gotJob ? true : null); + $status = $this->stopIfNecessary( + $options, $lastRestart, $startTime, $jobsProcessed, $job + ); + + if (! is_null($status)) { + return $this->stop($status); + } $this->gotJob = false; } From 768d4967289762bd50ffbbe8beb53ac47c212b33 Mon Sep 17 00:00:00 2001 From: Frans Saris Date: Mon, 1 Mar 2021 12:13:16 +0100 Subject: [PATCH 195/375] Remove return type void to reflect parent class --- src/Consumer.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Consumer.php b/src/Consumer.php index 3a8619ca..785fbfe2 100644 --- a/src/Consumer.php +++ b/src/Consumer.php @@ -54,7 +54,15 @@ public function setPrefetchCount(int $value): void $this->prefetchCount = $value; } - public function daemon($connectionName, $queue, WorkerOptions $options): void + /** + * Listen to the given queue in a loop. + * + * @param string $connectionName + * @param string $queue + * @param \Illuminate\Queue\WorkerOptions $options + * @return int + */ + public function daemon($connectionName, $queue, WorkerOptions $options) { if ($this->supportsAsyncSignals()) { $this->listenForSignals(); From ecd02f2af49b4cd917d61c0a0a60e4374433edb1 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Sun, 7 Mar 2021 00:31:46 +0100 Subject: [PATCH 196/375] fix errors in Consumer --- src/Consumer.php | 45 ++++++++++++++++++++----------------- src/Queue/RabbitMQQueue.php | 2 +- tests/Feature/TestCase.php | 3 ++- 3 files changed, 27 insertions(+), 23 deletions(-) diff --git a/src/Consumer.php b/src/Consumer.php index 785fbfe2..3690fcb4 100644 --- a/src/Consumer.php +++ b/src/Consumer.php @@ -4,13 +4,11 @@ use Exception; use Illuminate\Container\Container; -use Illuminate\Contracts\Queue\Job; use Illuminate\Queue\Worker; use Illuminate\Queue\WorkerOptions; use PhpAmqpLib\Channel\AMQPChannel; use PhpAmqpLib\Exception\AMQPRuntimeException; use PhpAmqpLib\Message\AMQPMessage; -use Symfony\Component\Debug\Exception\FatalThrowableError; use Throwable; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue; @@ -31,8 +29,8 @@ class Consumer extends Worker /** @var AMQPChannel */ protected $channel; - /** @var bool */ - protected $gotJob = false; + /** @var object|null */ + protected $currentJob; public function setContainer(Container $value): void { @@ -57,10 +55,11 @@ public function setPrefetchCount(int $value): void /** * Listen to the given queue in a loop. * - * @param string $connectionName - * @param string $queue - * @param \Illuminate\Queue\WorkerOptions $options + * @param string $connectionName + * @param string $queue + * @param WorkerOptions $options * @return int + * @throws Throwable */ public function daemon($connectionName, $queue, WorkerOptions $options) { @@ -70,6 +69,8 @@ public function daemon($connectionName, $queue, WorkerOptions $options) $lastRestart = $this->getTimestampOfLastQueueRestart(); + [$startTime, $jobsProcessed] = [hrtime(true) / 1e9, 0]; + /** @var RabbitMQQueue $connection */ $connection = $this->manager->connection($connectionName); @@ -90,9 +91,7 @@ public function daemon($connectionName, $queue, WorkerOptions $options) false, false, false, - function (AMQPMessage $message) use ($connection, $options, $connectionName, $queue, $jobClass): void { - $this->gotJob = true; - + function (AMQPMessage $message) use ($connection, $options, $connectionName, $queue, $jobClass, &$jobsProcessed): void { $job = new $jobClass( $this->container, $connection, @@ -101,10 +100,14 @@ function (AMQPMessage $message) use ($connection, $options, $connectionName, $qu $queue ); + $this->currentJob = $job; + if ($this->supportsAsyncSignals()) { $this->registerTimeoutHandler($job, $options); } + $jobsProcessed++; + $this->runJob($job, $connectionName, $options); if ($this->supportsAsyncSignals()) { @@ -129,18 +132,14 @@ function (AMQPMessage $message) use ($connection, $options, $connectionName, $qu $this->exceptions->report($exception); $this->kill(1); - } catch (Exception $exception) { + } catch (Exception | Throwable $exception) { $this->exceptions->report($exception); - $this->stopWorkerIfLostConnection($exception); - } catch (Throwable $exception) { - $this->exceptions->report($exception = new FatalThrowableError($exception)); - $this->stopWorkerIfLostConnection($exception); } // If no job is got off the queue, we will need to sleep the worker. - if (! $this->gotJob) { + if ($this->currentJob === null) { $this->sleep($options->sleep); } @@ -148,14 +147,18 @@ function (AMQPMessage $message) use ($connection, $options, $connectionName, $qu // the queue should restart based on other indications. If so, we'll stop // this worker and let whatever is "monitoring" it restart the process. $status = $this->stopIfNecessary( - $options, $lastRestart, $startTime, $jobsProcessed, $job + $options, + $lastRestart, + $startTime, + $jobsProcessed, + $this->currentJob ); if (! is_null($status)) { return $this->stop($status); } - $this->gotJob = false; + $this->currentJob = null; } } @@ -176,14 +179,14 @@ protected function daemonShouldRun(WorkerOptions $options, $connectionName, $que * Stop listening and bail out of the script. * * @param int $status - * @return void + * @return int */ - public function stop($status = 0): void + public function stop($status = 0): int { // Tell the server you are going to stop consuming. // It will finish up the last message and not send you any more. $this->channel->basic_cancel($this->consumerTag, false, true); - parent::stop($status); + return parent::stop($status); } } diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index 40578a1f..3b955d66 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -279,7 +279,7 @@ public function getChannel(): AMQPChannel * Job class to use. * * @return string - * @throws \Throwable + * @throws Throwable */ public function getJobClass(): string { diff --git a/tests/Feature/TestCase.php b/tests/Feature/TestCase.php index b24f13a0..0a387ef6 100644 --- a/tests/Feature/TestCase.php +++ b/tests/Feature/TestCase.php @@ -5,6 +5,7 @@ use Illuminate\Support\Facades\Queue; use Illuminate\Support\Str; use PhpAmqpLib\Exception\AMQPProtocolChannelException; +use RuntimeException; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Jobs\RabbitMQJob; use VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Mocks\TestJob; use VladimirYuldashev\LaravelQueueRabbitMQ\Tests\TestCase as BaseTestCase; @@ -310,7 +311,7 @@ public function testFailed(): void $job = Queue::pop(); - $job->fail(new \RuntimeException($job->resolveName().' has an exception.')); + $job->fail(new RuntimeException($job->resolveName().' has an exception.')); sleep(1); From 3a39d3569c0768253ebac90fb8d0fb061f2a0783 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Sun, 7 Mar 2021 00:36:58 +0100 Subject: [PATCH 197/375] update CHANGELOG-11x.md --- CHANGELOG-11x.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG-11x.md b/CHANGELOG-11x.md index ce013a50..962de6a0 100644 --- a/CHANGELOG-11x.md +++ b/CHANGELOG-11x.md @@ -2,7 +2,11 @@ All notable changes to this project will be documented in this file. -## [Unreleased](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v11.1.1...master) +## [Unreleased](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v11.1.2...master) + +## [11.1.2 (2021-03-07)](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v11.1.1...v11.1.2) + +- Update Consumer to stop when stopIfNecessary() returns exit code [#409](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/409) ## [11.1.1 (2020-12-07)](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v11.1.0...v11.1.1) From 7bc0ac5d6067c3e8259a1abdea872a3a6d13f253 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20B=C4=9Bhal?= Date: Tue, 16 Mar 2021 21:10:37 +0100 Subject: [PATCH 198/375] Fix missing rest option (#416) --- src/Console/ConsumeCommand.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Console/ConsumeCommand.php b/src/Console/ConsumeCommand.php index ab843f99..100be54a 100644 --- a/src/Console/ConsumeCommand.php +++ b/src/Console/ConsumeCommand.php @@ -23,6 +23,7 @@ class ConsumeCommand extends WorkCommand {--sleep=3 : Number of seconds to sleep when no job is available} {--timeout=60 : The number of seconds a child process can run} {--tries=1 : Number of times to attempt a job before logging it failed} + {--rest=0 : Number of seconds to rest between jobs} {--consumer-tag} {--prefetch-size=0} From a333efe259d30ec931f6d6955a8376a5f4adcef9 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Tue, 16 Mar 2021 21:11:26 +0100 Subject: [PATCH 199/375] php 8 support --- .github/workflows/tests.yml | 3 ++- CHANGELOG-11x.md | 7 ++++++- composer.json | 4 ++-- tests/Functional/RabbitMQQueueTest.php | 1 - 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index b31d03df..fa72ec07 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -13,7 +13,7 @@ jobs: strategy: fail-fast: true matrix: - php: [7.3, 7.4] + php: [7.3, 7.4, 8.0] stability: [prefer-lowest, prefer-stable] name: PHP ${{ matrix.php }} - ${{ matrix.stability }} @@ -43,6 +43,7 @@ jobs: run: composer update --${{ matrix.stability }} --prefer-dist --no-interaction --no-progress - name: Run PHP CS Fixer + if: ${{ matrix.php != 8.0 }} run: vendor/bin/php-cs-fixer fix --config=.php_cs.dist --allow-risky=yes --dry-run --diff --verbose - name: Execute tests diff --git a/CHANGELOG-11x.md b/CHANGELOG-11x.md index 962de6a0..f5a83a38 100644 --- a/CHANGELOG-11x.md +++ b/CHANGELOG-11x.md @@ -2,7 +2,12 @@ All notable changes to this project will be documented in this file. -## [Unreleased](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v11.1.2...master) +## [Unreleased](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v11.2.0...master) + +## [11.2.0 (2021-03-16)](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v11.1.2...v11.2.0) + +- PHP 8 support +- Fix missing rest option in `php artisan rabbitmq:consume` command [#416](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/416) ## [11.1.2 (2021-03-07)](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v11.1.1...v11.1.2) diff --git a/composer.json b/composer.json index b097891f..d9ed64fc 100644 --- a/composer.json +++ b/composer.json @@ -9,10 +9,10 @@ } ], "require": { - "php": "^7.3", + "php": "^7.3|^8.0", "ext-json": "*", "illuminate/queue": "^8.0", - "php-amqplib/php-amqplib": "^2.12" + "php-amqplib/php-amqplib": "^2.12|^3.0" }, "require-dev": { "phpunit/phpunit": "^9.3", diff --git a/tests/Functional/RabbitMQQueueTest.php b/tests/Functional/RabbitMQQueueTest.php index 086f116e..2c8484f7 100644 --- a/tests/Functional/RabbitMQQueueTest.php +++ b/tests/Functional/RabbitMQQueueTest.php @@ -70,7 +70,6 @@ public function testExchangeType(): void $queue = $this->connection(); $this->assertSame(AMQPExchangeType::DIRECT, $this->callMethod($queue, 'getExchangeType')); $this->assertSame(AMQPExchangeType::DIRECT, $this->callMethod($queue, 'getExchangeType', [''])); - $this->assertSame(AMQPExchangeType::DIRECT, $this->callMethod($queue, 'getExchangeType', ['test'])); $this->assertSame(AMQPExchangeType::TOPIC, $this->callMethod($queue, 'getExchangeType', ['topic'])); $queue = $this->connection('rabbitmq-with-options'); From d6b6a043d1f7fe45ffaa1bf41ff4a149520b530d Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Tue, 16 Mar 2021 21:20:37 +0100 Subject: [PATCH 200/375] fix ci --- .github/workflows/tests.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index fa72ec07..3de74a7c 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -13,9 +13,11 @@ jobs: strategy: fail-fast: true matrix: - php: [7.3, 7.4, 8.0] + php: ['7.3', '7.4', '8.0'] stability: [prefer-lowest, prefer-stable] + if: matrix.php != '8.0 && matrix.stability != 'prefer-lowest' + name: PHP ${{ matrix.php }} - ${{ matrix.stability }} steps: From fed393d5ae18bb3e63a76d5bbbcafb7c10ed7726 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Tue, 16 Mar 2021 21:22:24 +0100 Subject: [PATCH 201/375] fix ci --- .github/workflows/tests.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 3de74a7c..afcf10e8 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -15,8 +15,9 @@ jobs: matrix: php: ['7.3', '7.4', '8.0'] stability: [prefer-lowest, prefer-stable] - - if: matrix.php != '8.0 && matrix.stability != 'prefer-lowest' + exclude: + - php: '8.0' + stability: 'prefer-lowest' name: PHP ${{ matrix.php }} - ${{ matrix.stability }} From d7540514d6ea3d5b371e30538667b50ff53182e0 Mon Sep 17 00:00:00 2001 From: Sujit Date: Mon, 17 May 2021 13:30:39 +0545 Subject: [PATCH 202/375] Add priority for jobs --- src/Console/ConsumeCommand.php | 4 +++- src/Consumer.php | 12 +++++++++++- src/Queue/RabbitMQQueue.php | 10 ++++++++-- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/Console/ConsumeCommand.php b/src/Console/ConsumeCommand.php index 100be54a..4d47b697 100644 --- a/src/Console/ConsumeCommand.php +++ b/src/Console/ConsumeCommand.php @@ -24,7 +24,8 @@ class ConsumeCommand extends WorkCommand {--timeout=60 : The number of seconds a child process can run} {--tries=1 : Number of times to attempt a job before logging it failed} {--rest=0 : Number of seconds to rest between jobs} - + + {--max-priority=4} {--consumer-tag} {--prefetch-size=0} {--prefetch-count=1000} @@ -40,6 +41,7 @@ public function handle(): void $consumer->setContainer($this->laravel); $consumer->setName($this->option('name')); $consumer->setConsumerTag($this->consumerTag()); + $consumer->setMaxPriority((int) $this->option('max-priority')); $consumer->setPrefetchSize((int) $this->option('prefetch-size')); $consumer->setPrefetchCount((int) $this->option('prefetch-count')); diff --git a/src/Consumer.php b/src/Consumer.php index 3690fcb4..03a18780 100644 --- a/src/Consumer.php +++ b/src/Consumer.php @@ -23,6 +23,9 @@ class Consumer extends Worker /** @var int */ protected $prefetchSize; + /** @var int */ + protected $maxPriority; + /** @var int */ protected $prefetchCount; @@ -42,6 +45,11 @@ public function setConsumerTag(string $value): void $this->consumerTag = $value; } + public function setMaxPriority(int $value): void + { + $this->maxPriority = $value ?? 1; + } + public function setPrefetchSize(int $value): void { $this->prefetchSize = $value; @@ -113,7 +121,9 @@ function (AMQPMessage $message) use ($connection, $options, $connectionName, $qu if ($this->supportsAsyncSignals()) { $this->resetTimeoutHandler(); } - } + }, + null, + ['priority' => ['I', $this->maxPriority]] ); while ($this->channel->is_consuming()) { diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index 3b955d66..0f755b3a 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -530,16 +530,22 @@ protected function createMessage($payload, int $attempts = 0): array $properties = [ 'content_type' => 'application/json', 'delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT, + 'priority' => 1, ]; - if ($correlationId = json_decode($payload, true, 512)['id'] ?? null) { + $currentPayload = json_decode($payload, true, 512); + if ($correlationId = $currentPayload['id'] ?? null) { $properties['correlation_id'] = $correlationId; } if ($this->isPrioritizeDelayed()) { $properties['priority'] = $attempts; } - + $commandData = unserialize($currentPayload['data']['command']); + if(property_exists($commandData, 'priority')) + { + $properties['priority'] = $commandData->priority; + } $message = new AMQPMessage($payload, $properties); $message->set('application_headers', new AMQPTable([ From 8078f06d9c61916ab8c03cdfc05ba21e13639f65 Mon Sep 17 00:00:00 2001 From: Sujit Date: Mon, 17 May 2021 13:34:24 +0545 Subject: [PATCH 203/375] Fix indentation --- src/Queue/RabbitMQQueue.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index 0f755b3a..5bb11af6 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -542,8 +542,7 @@ protected function createMessage($payload, int $attempts = 0): array $properties['priority'] = $attempts; } $commandData = unserialize($currentPayload['data']['command']); - if(property_exists($commandData, 'priority')) - { + if(property_exists($commandData, 'priority')) { $properties['priority'] = $commandData->priority; } $message = new AMQPMessage($payload, $properties); From 91e01b8e6dd4edd4be3d1a0ed543a3b0123779e5 Mon Sep 17 00:00:00 2001 From: Sujit Date: Mon, 17 May 2021 13:35:10 +0545 Subject: [PATCH 204/375] Fix indentation --- src/Queue/RabbitMQQueue.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index 5bb11af6..a8f9a887 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -542,7 +542,7 @@ protected function createMessage($payload, int $attempts = 0): array $properties['priority'] = $attempts; } $commandData = unserialize($currentPayload['data']['command']); - if(property_exists($commandData, 'priority')) { + if (property_exists($commandData, 'priority')) { $properties['priority'] = $commandData->priority; } $message = new AMQPMessage($payload, $properties); From e1ecaf90f21b120863b6c169c7bebe93785598c5 Mon Sep 17 00:00:00 2001 From: Sujit Date: Mon, 17 May 2021 15:50:25 +0545 Subject: [PATCH 205/375] Remove `?? 1` since it's redundant --- src/Consumer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Consumer.php b/src/Consumer.php index 03a18780..0b759c30 100644 --- a/src/Consumer.php +++ b/src/Consumer.php @@ -47,7 +47,7 @@ public function setConsumerTag(string $value): void public function setMaxPriority(int $value): void { - $this->maxPriority = $value ?? 1; + $this->maxPriority = $value; } public function setPrefetchSize(int $value): void From 9cd95be5414873c8f861f301da40b84b2be95522 Mon Sep 17 00:00:00 2001 From: Sujit Date: Mon, 17 May 2021 16:49:43 +0545 Subject: [PATCH 206/375] Add Max priority for queue declare --- src/Console/QueueDeclareCommand.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Console/QueueDeclareCommand.php b/src/Console/QueueDeclareCommand.php index 0c89f329..99747a17 100644 --- a/src/Console/QueueDeclareCommand.php +++ b/src/Console/QueueDeclareCommand.php @@ -12,6 +12,7 @@ class QueueDeclareCommand extends Command {name : The name of the queue to declare} {connection=rabbitmq : The name of the queue connection to use} {--durable=1} + {--max-priority=4} {--auto-delete=0}'; protected $description = 'Declare queue'; @@ -31,11 +32,15 @@ public function handle(RabbitMQConnector $connector): void return; } - + $maxPriority = (int) $this->option('max-priority'); + $arguments = [ + 'x-max-priority' => $maxPriority + ]; $queue->declareQueue( $this->argument('name'), (bool) $this->option('durable'), - (bool) $this->option('auto-delete') + (bool) $this->option('auto-delete'), + $arguments ); $this->info('Queue declared successfully.'); From 8c43b67d1a979bf40a9ddefc62dd8b66230206d7 Mon Sep 17 00:00:00 2001 From: Sujit Date: Mon, 17 May 2021 16:50:22 +0545 Subject: [PATCH 207/375] Added comma --- src/Console/QueueDeclareCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Console/QueueDeclareCommand.php b/src/Console/QueueDeclareCommand.php index 99747a17..9af36ea5 100644 --- a/src/Console/QueueDeclareCommand.php +++ b/src/Console/QueueDeclareCommand.php @@ -34,7 +34,7 @@ public function handle(RabbitMQConnector $connector): void } $maxPriority = (int) $this->option('max-priority'); $arguments = [ - 'x-max-priority' => $maxPriority + 'x-max-priority' => $maxPriority, ]; $queue->declareQueue( $this->argument('name'), From 0aa6119c1cc1f637e784b1884f86de6096900081 Mon Sep 17 00:00:00 2001 From: Sujit Date: Thu, 20 May 2021 09:17:58 +0545 Subject: [PATCH 208/375] Add Default option to set Priority --- src/Console/ConsumeCommand.php | 2 +- src/Console/QueueDeclareCommand.php | 11 +++++++---- src/Consumer.php | 6 +++++- src/Queue/RabbitMQQueue.php | 1 - 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/Console/ConsumeCommand.php b/src/Console/ConsumeCommand.php index 4d47b697..dbb3c88a 100644 --- a/src/Console/ConsumeCommand.php +++ b/src/Console/ConsumeCommand.php @@ -25,7 +25,7 @@ class ConsumeCommand extends WorkCommand {--tries=1 : Number of times to attempt a job before logging it failed} {--rest=0 : Number of seconds to rest between jobs} - {--max-priority=4} + {--max-priority} {--consumer-tag} {--prefetch-size=0} {--prefetch-count=1000} diff --git a/src/Console/QueueDeclareCommand.php b/src/Console/QueueDeclareCommand.php index 9af36ea5..3803a1c4 100644 --- a/src/Console/QueueDeclareCommand.php +++ b/src/Console/QueueDeclareCommand.php @@ -12,7 +12,7 @@ class QueueDeclareCommand extends Command {name : The name of the queue to declare} {connection=rabbitmq : The name of the queue connection to use} {--durable=1} - {--max-priority=4} + {--max-priority} {--auto-delete=0}'; protected $description = 'Declare queue'; @@ -32,10 +32,13 @@ public function handle(RabbitMQConnector $connector): void return; } + + $arguments = []; $maxPriority = (int) $this->option('max-priority'); - $arguments = [ - 'x-max-priority' => $maxPriority, - ]; + if ($maxPriority) { + $arguments['x-max-priority'] = $maxPriority; + } + $queue->declareQueue( $this->argument('name'), (bool) $this->option('durable'), diff --git a/src/Consumer.php b/src/Consumer.php index 0b759c30..22cf5896 100644 --- a/src/Consumer.php +++ b/src/Consumer.php @@ -91,6 +91,10 @@ public function daemon($connectionName, $queue, WorkerOptions $options) ); $jobClass = $connection->getJobClass(); + $arguments = []; + if ($this->maxPriority) { + $arguments['priority'] = ['I', $this->maxPriority]; + } $this->channel->basic_consume( $queue, @@ -123,7 +127,7 @@ function (AMQPMessage $message) use ($connection, $options, $connectionName, $qu } }, null, - ['priority' => ['I', $this->maxPriority]] + $arguments ); while ($this->channel->is_consuming()) { diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index a8f9a887..14bd8325 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -530,7 +530,6 @@ protected function createMessage($payload, int $attempts = 0): array $properties = [ 'content_type' => 'application/json', 'delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT, - 'priority' => 1, ]; $currentPayload = json_decode($payload, true, 512); From 67a012bcd583def8b2a9fedc61cdda5066a29ecd Mon Sep 17 00:00:00 2001 From: Sujit Date: Thu, 20 May 2021 09:19:44 +0545 Subject: [PATCH 209/375] fix indentation --- src/Consumer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Consumer.php b/src/Consumer.php index 22cf5896..e6da04ef 100644 --- a/src/Consumer.php +++ b/src/Consumer.php @@ -93,7 +93,7 @@ public function daemon($connectionName, $queue, WorkerOptions $options) $jobClass = $connection->getJobClass(); $arguments = []; if ($this->maxPriority) { - $arguments['priority'] = ['I', $this->maxPriority]; + $arguments['priority'] = ['I', $this->maxPriority]; } $this->channel->basic_consume( From 06d96c31efecb8723d115bc28f71c355af2eae94 Mon Sep 17 00:00:00 2001 From: Sujit Date: Mon, 24 May 2021 20:35:26 +0545 Subject: [PATCH 210/375] fix bug for default max priority --- src/Console/ConsumeCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Console/ConsumeCommand.php b/src/Console/ConsumeCommand.php index dbb3c88a..8e92a4a3 100644 --- a/src/Console/ConsumeCommand.php +++ b/src/Console/ConsumeCommand.php @@ -25,7 +25,7 @@ class ConsumeCommand extends WorkCommand {--tries=1 : Number of times to attempt a job before logging it failed} {--rest=0 : Number of seconds to rest between jobs} - {--max-priority} + {--max-priority=} {--consumer-tag} {--prefetch-size=0} {--prefetch-count=1000} From ae7de0747439a26d14a299de2bbbaef089e0a7a0 Mon Sep 17 00:00:00 2001 From: Merhnferr Date: Tue, 6 Jul 2021 02:16:43 +0300 Subject: [PATCH 211/375] [10.2.2] Possibility to declare quorum queue. (#359) * Added: possibility to declare quorum queue using connection config or artisan command * Ignore priority argument for quorum queue. * Fixed style Co-authored-by: Merhnferr --- src/Console/QueueDeclareCommand.php | 10 +++++-- src/Queue/RabbitMQQueue.php | 17 ++++++++++- tests/Functional/RabbitMQQueueTest.php | 27 ++++++++++++++++++ tests/Functional/TestCase.php | 39 ++++++++++++++++++++++++++ 4 files changed, 90 insertions(+), 3 deletions(-) diff --git a/src/Console/QueueDeclareCommand.php b/src/Console/QueueDeclareCommand.php index 0c89f329..22c98cdf 100644 --- a/src/Console/QueueDeclareCommand.php +++ b/src/Console/QueueDeclareCommand.php @@ -12,7 +12,8 @@ class QueueDeclareCommand extends Command {name : The name of the queue to declare} {connection=rabbitmq : The name of the queue connection to use} {--durable=1} - {--auto-delete=0}'; + {--auto-delete=0} + {--quorum=0}'; protected $description = 'Declare queue'; @@ -32,10 +33,15 @@ public function handle(RabbitMQConnector $connector): void return; } + $arguments = (bool) $this->option('quorum') + ? ['x-queue-type' => 'quorum'] + : []; + $queue->declareQueue( $this->argument('name'), (bool) $this->option('durable'), - (bool) $this->option('auto-delete') + (bool) $this->option('auto-delete'), + $arguments ); $this->info('Queue declared successfully.'); diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index 3b955d66..c7f51d9c 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -611,7 +611,8 @@ protected function getQueueArguments(string $destination): array // Messages without a priority property are treated as if their priority were 0. // Messages with a priority which is higher than the queue's maximum, are treated as if they were // published with the maximum priority. - if ($this->isPrioritizeDelayed()) { + // Quorum queues does not support priority. + if ($this->isPrioritizeDelayed() && ! $this->isQuorum()) { $arguments['x-max-priority'] = $this->getQueueMaxPriority(); } @@ -620,6 +621,10 @@ protected function getQueueArguments(string $destination): array $arguments['x-dead-letter-routing-key'] = $this->getFailedRoutingKey($destination); } + if ($this->isQuorum()) { + $arguments['x-queue-type'] = 'quorum'; + } + return $arguments; } @@ -710,6 +715,16 @@ protected function isRerouteFailed(): bool return (bool) (Arr::get($this->options, 'reroute_failed') ?: false); } + /** + * Returns &true;, if declared queue must be quorum queue. + * + * @return bool + */ + protected function isQuorum(): bool + { + return (bool) (Arr::get($this->options, 'quorum') ?: false); + } + /** * Get the exchange for failed messages. * diff --git a/tests/Functional/RabbitMQQueueTest.php b/tests/Functional/RabbitMQQueueTest.php index 2c8484f7..1c03fe99 100644 --- a/tests/Functional/RabbitMQQueueTest.php +++ b/tests/Functional/RabbitMQQueueTest.php @@ -139,6 +139,22 @@ public function testFailedRoutingKey(): void $this->assertSame('test.failed', $this->callMethod($queue, 'getFailedRoutingKey', ['test'])); } + public function testQuorum(): void + { + /** @var $queue RabbitMQQueue */ + $queue = $this->connection(); + $this->assertFalse($this->callMethod($queue, 'isQuorum')); + + $queue = $this->connection('rabbitmq-with-options'); + $this->assertFalse($this->callMethod($queue, 'isQuorum')); + + $queue = $this->connection('rabbitmq-with-quorum-options'); + $this->assertTrue($this->callMethod($queue, 'isQuorum')); + + $queue = $this->connection('rabbitmq-with-options-empty'); + $this->assertFalse($this->callMethod($queue, 'isQuorum')); + } + public function testDeclareDeleteExchange(): void { /** @var $queue RabbitMQQueue */ @@ -193,6 +209,17 @@ public function testQueueArguments(): void $this->assertEquals(array_keys($expected), array_keys($actual)); $this->assertEquals(array_values($expected), array_values($actual)); + $queue = $this->connection('rabbitmq-with-quorum-options'); + $actual = $this->callMethod($queue, 'getQueueArguments', [$name]); + $expected = [ + 'x-dead-letter-exchange' => 'failed-exchange', + 'x-dead-letter-routing-key' => sprintf('application-x.%s.failed', $name), + 'x-queue-type' => 'quorum', + ]; + + $this->assertEquals(array_keys($expected), array_keys($actual)); + $this->assertEquals(array_values($expected), array_values($actual)); + $queue = $this->connection('rabbitmq-with-options-empty'); $actual = $this->callMethod($queue, 'getQueueArguments', [$name]); $expected = []; diff --git a/tests/Functional/TestCase.php b/tests/Functional/TestCase.php index b7f5b48b..449d5b8e 100644 --- a/tests/Functional/TestCase.php +++ b/tests/Functional/TestCase.php @@ -110,6 +110,45 @@ protected function getEnvironmentSetUp($app): void 'reroute_failed' => '', 'failed_exchange' => '', 'failed_routing_key' => '', + 'quorum' => '', + ], + ], + + 'worker' => 'default', + + ]); + $app['config']->set('queue.connections.rabbitmq-with-quorum-options', [ + 'driver' => 'rabbitmq', + 'queue' => 'order', + 'connection' => AMQPLazyConnection::class, + + 'hosts' => [ + [ + 'host' => getenv('HOST'), + 'port' => getenv('PORT'), + 'vhost' => '/', + 'user' => 'guest', + 'password' => 'guest', + ], + ], + + 'options' => [ + 'ssl_options' => [ + 'cafile' => null, + 'local_cert' => null, + 'local_key' => null, + 'verify_peer' => true, + 'passphrase' => null, + ], + + 'queue' => [ + 'exchange' => 'application-x', + 'exchange_type' => 'topic', + 'exchange_routing_key' => 'process.%s', + 'reroute_failed' => true, + 'failed_exchange' => 'failed-exchange', + 'failed_routing_key' => 'application-x.%s.failed', + 'quorum' => true, ], ], From 2351f7223c296567806834d47589953eb93176a3 Mon Sep 17 00:00:00 2001 From: Khalil Laleh Date: Tue, 6 Jul 2021 03:49:02 +0430 Subject: [PATCH 212/375] Update the custom class code in the README file (#427) --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 8328df6f..aa62b83e 100644 --- a/README.md +++ b/README.md @@ -215,6 +215,8 @@ class RabbitMQJob extends BaseJob $method = 'handle'; ($this->instance = $this->resolve($class))->{$method}($this, $payload); + + $this->delete(); } } From 93522f76ba585e9e59f38d89bcb8004c8b34867b Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Tue, 6 Jul 2021 01:21:54 +0200 Subject: [PATCH 213/375] update changelog --- CHANGELOG-11x.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG-11x.md b/CHANGELOG-11x.md index f5a83a38..7fea012b 100644 --- a/CHANGELOG-11x.md +++ b/CHANGELOG-11x.md @@ -2,7 +2,12 @@ All notable changes to this project will be documented in this file. -## [Unreleased](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v11.2.0...master) +## [Unreleased](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v11.3.0...master) + +## [11.3.0 (2021-07-06)](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v11.2.0...v11.3.0) + +- Quorum queues support [#359](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/359) +- max-priority support [#422](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/422) ## [11.2.0 (2021-03-16)](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v11.1.2...v11.2.0) From e073ea4d0a3442838ee92d9014c06b0234e6920c Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Tue, 6 Jul 2021 01:28:01 +0200 Subject: [PATCH 214/375] resolve conflicts --- .php_cs.dist => .php-cs-fixer.dist.php | 10 +++++----- composer.json | 4 ++-- src/Console/QueueDeclareCommand.php | 2 +- src/Consumer.php | 1 + src/Queue/Connectors/RabbitMQConnector.php | 2 ++ tests/Functional/TestCase.php | 11 +++++++---- 6 files changed, 18 insertions(+), 12 deletions(-) rename .php_cs.dist => .php-cs-fixer.dist.php (88%) diff --git a/.php_cs.dist b/.php-cs-fixer.dist.php similarity index 88% rename from .php_cs.dist rename to .php-cs-fixer.dist.php index 930c161e..6b56b57b 100644 --- a/.php_cs.dist +++ b/.php-cs-fixer.dist.php @@ -7,7 +7,7 @@ ->setUsingCache(false) ->setFinder($finder) ->setRules([ - 'psr0' => false, + 'psr_autoloading' => false, '@PSR2' => true, 'blank_line_after_namespace' => true, 'braces' => true, @@ -17,10 +17,10 @@ 'function_declaration' => true, 'indentation_type' => true, 'line_ending' => true, - 'lowercase_constants' => true, + 'constant_case' => true, 'lowercase_keywords' => true, 'method_argument_space' => [ - 'ensure_fully_multiline' => true, + 'on_multiline' => 'ensure_fully_multiline', ], 'no_break_comment' => true, 'no_closing_tag' => true, @@ -39,9 +39,9 @@ 'visibility_required' => true, 'encoding' => true, 'full_opening_tag' => true, - 'blank_line_before_return' => true, + 'blank_line_before_statement' => true, 'no_trailing_comma_in_singleline_array' => true, - 'trailing_comma_in_multiline_array' => true, + 'trailing_comma_in_multiline' => true, 'array_indentation' => true, 'binary_operator_spaces' => [ 'operators' => [ diff --git a/composer.json b/composer.json index d9ed64fc..90d5edf4 100644 --- a/composer.json +++ b/composer.json @@ -49,9 +49,9 @@ "@test:style", "@test:unit" ], - "test:style": "@php vendor/bin/php-cs-fixer fix --config=.php_cs.dist --allow-risky=yes --dry-run --diff --verbose", + "test:style": "@php vendor/bin/php-cs-fixer fix --config=.php-cs-fixer.dist.php --allow-risky=yes --dry-run --diff --verbose", "test:unit": "@php vendor/bin/phpunit", - "fix:style": "@php vendor/bin/php-cs-fixer fix --config=.php_cs.dist --allow-risky=yes --diff --verbose" + "fix:style": "@php vendor/bin/php-cs-fixer fix --config=.php-cs-fixer.dist.php --allow-risky=yes --diff --verbose" }, "minimum-stability": "dev", "prefer-stable": true diff --git a/src/Console/QueueDeclareCommand.php b/src/Console/QueueDeclareCommand.php index 87c87a41..97742c28 100644 --- a/src/Console/QueueDeclareCommand.php +++ b/src/Console/QueueDeclareCommand.php @@ -41,7 +41,7 @@ public function handle(RabbitMQConnector $connector): void $arguments['x-max-priority'] = $maxPriority; } - if($this->option('quorum')) { + if ($this->option('quorum')) { $arguments['x-queue-type'] = 'quorum'; } diff --git a/src/Consumer.php b/src/Consumer.php index e6da04ef..e32b3efc 100644 --- a/src/Consumer.php +++ b/src/Consumer.php @@ -136,6 +136,7 @@ function (AMQPMessage $message) use ($connection, $options, $connectionName, $qu // make sure we do not need to kill this worker process off completely. if (! $this->daemonShouldRun($options, $connectionName, $queue)) { $this->pauseWorker($options, $lastRestart); + continue; } diff --git a/src/Queue/Connectors/RabbitMQConnector.php b/src/Queue/Connectors/RabbitMQConnector.php index e39f7b9a..a6958082 100644 --- a/src/Queue/Connectors/RabbitMQConnector.php +++ b/src/Queue/Connectors/RabbitMQConnector.php @@ -113,12 +113,14 @@ private function filter(array $array): array foreach ($array as $index => &$value) { if (is_array($value)) { $value = $this->filter($value); + continue; } // If the value is null then remove it. if ($value === null) { unset($array[$index]); + continue; } } diff --git a/tests/Functional/TestCase.php b/tests/Functional/TestCase.php index 449d5b8e..ace2c497 100644 --- a/tests/Functional/TestCase.php +++ b/tests/Functional/TestCase.php @@ -2,7 +2,10 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Functional; +use Exception; use PhpAmqpLib\Connection\AMQPLazyConnection; +use ReflectionClass; +use ReflectionException; use VladimirYuldashev\LaravelQueueRabbitMQ\Tests\TestCase as BaseTestCase; abstract class TestCase extends BaseTestCase @@ -162,15 +165,15 @@ protected function getEnvironmentSetUp($app): void * @param string $method * @param array $parameters * @return mixed - * @throws \Exception + * @throws Exception */ protected function callMethod($object, string $method, array $parameters = []) { try { $className = get_class($object); - $reflection = new \ReflectionClass($className); - } catch (\ReflectionException $e) { - throw new \Exception($e->getMessage()); + $reflection = new ReflectionClass($className); + } catch (ReflectionException $e) { + throw new Exception($e->getMessage()); } $method = $reflection->getMethod($method); From 221a0dd2bcc57d7a7b4177d28676fb3f790f952a Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Tue, 6 Jul 2021 01:29:22 +0200 Subject: [PATCH 215/375] fix ci --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index afcf10e8..6a4535ad 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -47,7 +47,7 @@ jobs: - name: Run PHP CS Fixer if: ${{ matrix.php != 8.0 }} - run: vendor/bin/php-cs-fixer fix --config=.php_cs.dist --allow-risky=yes --dry-run --diff --verbose + run: vendor/bin/php-cs-fixer fix --config=.php-cs-fixer.dist.php --allow-risky=yes --dry-run --diff --verbose - name: Execute tests run: vendor/bin/phpunit --verbose From 7abe02ad26126412a75237558dcd1dffc0ea8b85 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Tue, 6 Jul 2021 01:31:20 +0200 Subject: [PATCH 216/375] fix ci --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 6a4535ad..6eef4993 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -50,4 +50,4 @@ jobs: run: vendor/bin/php-cs-fixer fix --config=.php-cs-fixer.dist.php --allow-risky=yes --dry-run --diff --verbose - name: Execute tests - run: vendor/bin/phpunit --verbose + run: sleep 10 && vendor/bin/phpunit --verbose From d9e861202920cba808d84de7ab0ccb9072f80805 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Tue, 6 Jul 2021 01:33:03 +0200 Subject: [PATCH 217/375] fix ci --- .php-cs-fixer.dist.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php index 6b56b57b..7bf5267e 100644 --- a/.php-cs-fixer.dist.php +++ b/.php-cs-fixer.dist.php @@ -41,7 +41,7 @@ 'full_opening_tag' => true, 'blank_line_before_statement' => true, 'no_trailing_comma_in_singleline_array' => true, - 'trailing_comma_in_multiline' => true, + 'trailing_comma_in_multiline_array' => true, 'array_indentation' => true, 'binary_operator_spaces' => [ 'operators' => [ From 0b090c529142d80ed6e977d289783a96e3f0e228 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Tue, 6 Jul 2021 01:56:46 +0200 Subject: [PATCH 218/375] fix test --- src/Queue/RabbitMQQueue.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index ba17d2d5..2c3086cd 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -540,10 +540,14 @@ protected function createMessage($payload, int $attempts = 0): array if ($this->isPrioritizeDelayed()) { $properties['priority'] = $attempts; } - $commandData = unserialize($currentPayload['data']['command']); - if (property_exists($commandData, 'priority')) { - $properties['priority'] = $commandData->priority; + + if(isset($currentPayload['data']['command'])) { + $commandData = unserialize($currentPayload['data']['command']); + if (property_exists($commandData, 'priority')) { + $properties['priority'] = $commandData->priority; + } } + $message = new AMQPMessage($payload, $properties); $message->set('application_headers', new AMQPTable([ From 115040a91f47d1e0599b4ad4bc41c5209880bfdb Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Tue, 6 Jul 2021 01:58:04 +0200 Subject: [PATCH 219/375] fix test --- src/Queue/RabbitMQQueue.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index 2c3086cd..bd208578 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -541,7 +541,7 @@ protected function createMessage($payload, int $attempts = 0): array $properties['priority'] = $attempts; } - if(isset($currentPayload['data']['command'])) { + if (isset($currentPayload['data']['command'])) { $commandData = unserialize($currentPayload['data']['command']); if (property_exists($commandData, 'priority')) { $properties['priority'] = $commandData->priority; From 7813a2d2cf31a34984a82aad84a28cb13b7718a6 Mon Sep 17 00:00:00 2001 From: Michiel Kempen Date: Tue, 6 Jul 2021 02:01:10 +0200 Subject: [PATCH 220/375] Add ability to specify exchange and exchange_type when using pushRaw() (#420) --- src/Queue/RabbitMQQueue.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index bd208578..ccbeb48b 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -824,8 +824,8 @@ protected function publishProperties($queue, array $options = []): array $attempts = Arr::get($options, 'attempts') ?: 0; $destination = $this->getRoutingKey($queue); - $exchange = $this->getExchange(); - $exchangeType = $this->getExchangeType(); + $exchange = $this->getExchange(Arr::get($options, 'exchange')); + $exchangeType = $this->getExchangeType(Arr::get($options, 'exchange_type')); return [$destination, $exchange, $exchangeType, $attempts]; } From 0728ff00c0f0a258b544e91ddac9a83611602394 Mon Sep 17 00:00:00 2001 From: Sebastien Armand Date: Mon, 5 Jul 2021 17:04:26 -0700 Subject: [PATCH 221/375] Remember exchanges once they have been verified (#407) * Remember exchanges once they have been verified * Remove remembered exchange on delete * remove by value --- src/Queue/RabbitMQQueue.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index ccbeb48b..2ab5d6a2 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -315,12 +315,17 @@ public function getQueue($queue = null): string */ public function isExchangeExists(string $exchange): bool { + if ($this->isExchangeDeclared($exchange)) { + return true; + } try { // create a temporary channel, so the main channel will not be closed on exception $channel = $this->connection->channel(); $channel->exchange_declare($exchange, '', true); $channel->close(); + $this->exchanges[] = $exchange; + return true; } catch (AMQPProtocolChannelException $exception) { if ($exception->amqp_reply_code === 404) { @@ -378,6 +383,9 @@ public function deleteExchange(string $name, bool $unused = false): void return; } + $idx = array_search($name, $this->exchanges); + unset($this->exchanges[$idx]); + $this->channel->exchange_delete( $name, $unused From 0dc0d9c31e95ca690a3e27ffc57d729d22f60979 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Tue, 6 Jul 2021 02:04:50 +0200 Subject: [PATCH 222/375] fix test --- CHANGELOG-11x.md | 2 ++ src/Queue/RabbitMQQueue.php | 1 + 2 files changed, 3 insertions(+) diff --git a/CHANGELOG-11x.md b/CHANGELOG-11x.md index 7fea012b..a8b74706 100644 --- a/CHANGELOG-11x.md +++ b/CHANGELOG-11x.md @@ -8,6 +8,8 @@ All notable changes to this project will be documented in this file. - Quorum queues support [#359](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/359) - max-priority support [#422](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/422) +- Ability to specify exchange and exchange_type when using pushRaw()[#420](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/420) +- Remember exchanges once they have been verified [#407](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/407) ## [11.2.0 (2021-03-16)](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v11.1.2...v11.2.0) diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index 2ab5d6a2..2b855fed 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -318,6 +318,7 @@ public function isExchangeExists(string $exchange): bool if ($this->isExchangeDeclared($exchange)) { return true; } + try { // create a temporary channel, so the main channel will not be closed on exception $channel = $this->connection->channel(); From a5649faabfd224365b7c363f5e449777316d8627 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Tue, 6 Jul 2021 02:05:52 +0200 Subject: [PATCH 223/375] update changelog --- CHANGELOG-11x.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG-11x.md b/CHANGELOG-11x.md index a8b74706..e05d96f8 100644 --- a/CHANGELOG-11x.md +++ b/CHANGELOG-11x.md @@ -8,7 +8,7 @@ All notable changes to this project will be documented in this file. - Quorum queues support [#359](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/359) - max-priority support [#422](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/422) -- Ability to specify exchange and exchange_type when using pushRaw()[#420](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/420) +- Ability to specify exchange and exchange_type when using pushRaw() [#420](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/420) - Remember exchanges once they have been verified [#407](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/407) ## [11.2.0 (2021-03-16)](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v11.1.2...v11.2.0) From 86ad27be9108f8d177447de14572781658fbe4a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Gamez?= Date: Tue, 27 Jul 2021 01:04:14 +0200 Subject: [PATCH 224/375] Randomize consumer tag (#432) --- src/Console/ConsumeCommand.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Console/ConsumeCommand.php b/src/Console/ConsumeCommand.php index 8e92a4a3..355c2e4b 100644 --- a/src/Console/ConsumeCommand.php +++ b/src/Console/ConsumeCommand.php @@ -54,6 +54,12 @@ protected function consumerTag(): string return $consumerTag; } - return Str::slug(config('app.name', 'laravel'), '_').'_'.getmypid(); + $consumerTag = implode('_', [ + Str::slug(config('app.name', 'laravel')), + Str::slug($this->option('name')), + md5(serialize($this->options()).Str::random(16).getmypid()), + ]); + + return Str::substr($consumerTag, 0, 255); } } From cda393e61b1f9f86506a31291eacb844d499f847 Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Tue, 27 Jul 2021 01:05:42 +0200 Subject: [PATCH 225/375] update changelog --- CHANGELOG-11x.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG-11x.md b/CHANGELOG-11x.md index e05d96f8..cbbc691e 100644 --- a/CHANGELOG-11x.md +++ b/CHANGELOG-11x.md @@ -2,14 +2,18 @@ All notable changes to this project will be documented in this file. -## [Unreleased](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v11.3.0...master) +## [Unreleased](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v11.4.0...master) + +## [11.4.0 (2021-07-27)](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v11.3.0...v11.4.0) + +- Randomize consumer tag [#432](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/432) ## [11.3.0 (2021-07-06)](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v11.2.0...v11.3.0) - Quorum queues support [#359](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/359) - max-priority support [#422](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/422) - Ability to specify exchange and exchange_type when using pushRaw() [#420](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/420) -- Remember exchanges once they have been verified [#407](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/407) +- Remember exchanges once they have been verified [#407](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/407) ## [11.2.0 (2021-03-16)](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v11.1.2...v11.2.0) From c8a722b38ae0c4625bc6c53df09c7742c5cfeb8b Mon Sep 17 00:00:00 2001 From: vyuldashev Date: Wed, 23 Feb 2022 02:37:45 +0100 Subject: [PATCH 226/375] laravel 9 --- CHANGELOG-12x.md | 10 ++++++++++ composer.json | 10 +++++----- 2 files changed, 15 insertions(+), 5 deletions(-) create mode 100644 CHANGELOG-12x.md diff --git a/CHANGELOG-12x.md b/CHANGELOG-12x.md new file mode 100644 index 00000000..750c82a0 --- /dev/null +++ b/CHANGELOG-12x.md @@ -0,0 +1,10 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +## [Unreleased](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v12.0.0...master) + +## [12.0.0 (2022-02-23)](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v11.4.0...v12.0.0) + +- Laravel 9 support +- Minimum PHP version is set to 8.0 diff --git a/composer.json b/composer.json index 90d5edf4..3873bfbf 100644 --- a/composer.json +++ b/composer.json @@ -9,17 +9,17 @@ } ], "require": { - "php": "^7.3|^8.0", + "php": "^8.0", "ext-json": "*", - "illuminate/queue": "^8.0", - "php-amqplib/php-amqplib": "^2.12|^3.0" + "illuminate/queue": "^9.0", + "php-amqplib/php-amqplib": "^3.0" }, "require-dev": { "phpunit/phpunit": "^9.3", "mockery/mockery": "^1.0", "laravel/horizon": "^5.0", - "friendsofphp/php-cs-fixer": "^2.17", - "orchestra/testbench": "^6.0" + "friendsofphp/php-cs-fixer": "^3.6", + "orchestra/testbench": "^7.0" }, "autoload": { "psr-4": { From 10e151f0c5264f517b8779ef9b38b1f6334fdd92 Mon Sep 17 00:00:00 2001 From: vyuldashev Date: Wed, 23 Feb 2022 02:43:48 +0100 Subject: [PATCH 227/375] laravel 9 --- .github/workflows/tests.yml | 5 +---- docker-compose.yml | 10 +++++----- tests/Feature/ConnectorTest.php | 2 ++ tests/Feature/SslQueueTest.php | 7 +++++++ 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 6eef4993..059be9ee 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -13,11 +13,8 @@ jobs: strategy: fail-fast: true matrix: - php: ['7.3', '7.4', '8.0'] + php: ['8.0', '8.1'] stability: [prefer-lowest, prefer-stable] - exclude: - - php: '8.0' - stability: 'prefer-lowest' name: PHP ${{ matrix.php }} - ${{ matrix.stability }} diff --git a/docker-compose.yml b/docker-compose.yml index 6052f7f9..4ac32caa 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,7 +3,7 @@ version: '3.7' services: rabbitmq: - image: rabbitmq + image: rabbitmq:3.8 environment: RABBITMQ_DEFAULT_USER: guest RABBITMQ_DEFAULT_PASSWORD: guest @@ -17,10 +17,10 @@ services: - "./tests/files/rootCA.pem:/rootCA.pem:ro" - "./tests/files/rootCA.key:/rootCA.key:ro" ports: - - 15671:15671 - - 15672:15672 - - 5671:5671 - - 5672:5672 + - "15671:15671" + - "15672:15672" + - "5671:5671" + - "5672:5672" rabbitmq-management: image: rabbitmq:management diff --git a/tests/Feature/ConnectorTest.php b/tests/Feature/ConnectorTest.php index 2c58874a..499018e9 100644 --- a/tests/Feature/ConnectorTest.php +++ b/tests/Feature/ConnectorTest.php @@ -53,6 +53,8 @@ public function testLazyConnection(): void public function testSslConnection(): void { + $this->markTestSkipped(); + $this->app['config']->set('queue.connections.rabbitmq', [ 'driver' => 'rabbitmq', 'queue' => env('RABBITMQ_QUEUE', 'default'), diff --git a/tests/Feature/SslQueueTest.php b/tests/Feature/SslQueueTest.php index 54969a9c..5f53ffc4 100644 --- a/tests/Feature/SslQueueTest.php +++ b/tests/Feature/SslQueueTest.php @@ -9,6 +9,13 @@ */ class SslQueueTest extends TestCase { + public function setUp(): void + { + parent::setUp(); + + $this->markTestSkipped(); + } + protected function getEnvironmentSetUp($app): void { $app['config']->set('queue.default', 'rabbitmq'); From 5091090ccffc6add54140f1d07c2cb256135fa1b Mon Sep 17 00:00:00 2001 From: vyuldashev Date: Wed, 23 Feb 2022 02:45:14 +0100 Subject: [PATCH 228/375] laravel 9 --- .php-cs-fixer.dist.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php index 7bf5267e..b1a2c0e9 100644 --- a/.php-cs-fixer.dist.php +++ b/.php-cs-fixer.dist.php @@ -3,7 +3,7 @@ $finder = PhpCsFixer\Finder::create() ->in(['config', 'src', 'tests']); -return PhpCsFixer\Config::create() +return (new PhpCsFixer\Config()) ->setUsingCache(false) ->setFinder($finder) ->setRules([ @@ -41,7 +41,7 @@ 'full_opening_tag' => true, 'blank_line_before_statement' => true, 'no_trailing_comma_in_singleline_array' => true, - 'trailing_comma_in_multiline_array' => true, + 'trailing_comma_in_multiline' => true, 'array_indentation' => true, 'binary_operator_spaces' => [ 'operators' => [ From 586d230e2cbac0f5d8838224c2a0adeac88938cf Mon Sep 17 00:00:00 2001 From: vyuldashev Date: Wed, 23 Feb 2022 02:49:16 +0100 Subject: [PATCH 229/375] laravel 9 --- tests/Feature/SslQueueTest.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/Feature/SslQueueTest.php b/tests/Feature/SslQueueTest.php index 5f53ffc4..7233ee09 100644 --- a/tests/Feature/SslQueueTest.php +++ b/tests/Feature/SslQueueTest.php @@ -11,8 +11,6 @@ class SslQueueTest extends TestCase { public function setUp(): void { - parent::setUp(); - $this->markTestSkipped(); } From f4db0b5b6d7c1ed1972197e8b70ebebaf0335da3 Mon Sep 17 00:00:00 2001 From: Morten Hauberg-Lund Date: Thu, 10 Mar 2022 16:56:51 +0100 Subject: [PATCH 230/375] Document missing support for multiple queues while using php artisan rabbitmq:consume --- README.md | 2 +- src/Console/ConsumeCommand.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index aa62b83e..9d889808 100644 --- a/README.md +++ b/README.md @@ -270,7 +270,7 @@ There are two ways of consuming messages. 1. `queue:work` command which is Laravel's built-in command. This command utilizes `basic_get`. -2. `rabbitmq:consume` command which is provided by this package. This command utilizes `basic_consume` and is more performant than `basic_get` by ~2x. +2. `rabbitmq:consume` command which is provided by this package. This command utilizes `basic_consume` and is more performant than `basic_get` by ~2x, but does not support multiple queues. ## Testing diff --git a/src/Console/ConsumeCommand.php b/src/Console/ConsumeCommand.php index 355c2e4b..50aab1d4 100644 --- a/src/Console/ConsumeCommand.php +++ b/src/Console/ConsumeCommand.php @@ -11,7 +11,7 @@ class ConsumeCommand extends WorkCommand protected $signature = 'rabbitmq:consume {connection? : The name of the queue connection to work} {--name=default : The name of the consumer} - {--queue= : The names of the queues to work} + {--queue= : The name of the queue to work. Please notice that there is no support for multiple queues} {--once : Only process the next job on the queue} {--stop-when-empty : Stop when the queue is empty} {--delay=0 : The number of seconds to delay failed jobs (Deprecated)} From 7c7ac39d04e897a21797db21e2b1568bd14bac70 Mon Sep 17 00:00:00 2001 From: Makarov Pavel Date: Thu, 7 Apr 2022 00:49:41 +0300 Subject: [PATCH 231/375] Exception thrown by ::pop is checked by Illuminate\Database\DetectsLostConnections::causedByLostConnection (#457) It should throw exception with special message in order to end queue on error correctly. Otherwise, it continues to work with broken connection. --- src/Queue/RabbitMQQueue.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index 2b855fed..4a684a5d 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -13,7 +13,10 @@ use JsonException; use PhpAmqpLib\Channel\AMQPChannel; use PhpAmqpLib\Connection\AbstractConnection; +use PhpAmqpLib\Exception\AMQPChannelClosedException; +use PhpAmqpLib\Exception\AMQPConnectionClosedException; use PhpAmqpLib\Exception\AMQPProtocolChannelException; +use PhpAmqpLib\Exception\AMQPRuntimeException; use PhpAmqpLib\Exchange\AMQPExchangeType; use PhpAmqpLib\Message\AMQPMessage; use PhpAmqpLib\Wire\AMQPTable; @@ -254,6 +257,16 @@ public function pop($queue = null) } throw $exception; + } catch (AMQPChannelClosedException|AMQPConnectionClosedException $exception) { + // Queue::pop used by worker to receive new job + // Thrown exception is checked by Illuminate\Database\DetectsLostConnections::causedByLostConnection + // Is has to contain one of the several phrases in exception message in order to restart worker + // Otherwise worker continues to work with broken connection + throw new AMQPRuntimeException( + 'Lost connection: '.$exception->getMessage(), + $exception->getCode(), + $exception + ); } return null; From 7ff9188c3f04a1358ae54d85a81085f5e96dfc0e Mon Sep 17 00:00:00 2001 From: Vladimir Yuldashev Date: Wed, 6 Apr 2022 23:51:59 +0200 Subject: [PATCH 232/375] Apply fixes from StyleCI (#473) Co-authored-by: StyleCI Bot --- src/Console/ExchangeDeclareCommand.php | 3 +- src/Console/ExchangeDeleteCommand.php | 3 +- src/Console/QueueBindCommand.php | 3 +- src/Console/QueueDeclareCommand.php | 3 +- src/Console/QueueDeleteCommand.php | 3 +- src/Console/QueuePurgeCommand.php | 3 +- src/Consumer.php | 15 +-- src/Horizon/Listeners/RabbitMQFailedEvent.php | 4 +- src/Horizon/RabbitMQQueue.php | 13 +- src/Queue/Connectors/RabbitMQConnector.php | 17 +-- src/Queue/Jobs/RabbitMQJob.php | 3 +- src/Queue/RabbitMQQueue.php | 112 ++++++++++-------- tests/Functional/TestCase.php | 5 +- 13 files changed, 105 insertions(+), 82 deletions(-) diff --git a/src/Console/ExchangeDeclareCommand.php b/src/Console/ExchangeDeclareCommand.php index 955da0a9..99f0b753 100644 --- a/src/Console/ExchangeDeclareCommand.php +++ b/src/Console/ExchangeDeclareCommand.php @@ -18,7 +18,8 @@ class ExchangeDeclareCommand extends Command protected $description = 'Declare exchange'; /** - * @param RabbitMQConnector $connector + * @param RabbitMQConnector $connector + * * @throws Exception */ public function handle(RabbitMQConnector $connector): void diff --git a/src/Console/ExchangeDeleteCommand.php b/src/Console/ExchangeDeleteCommand.php index af4473a2..904f38ba 100644 --- a/src/Console/ExchangeDeleteCommand.php +++ b/src/Console/ExchangeDeleteCommand.php @@ -16,7 +16,8 @@ class ExchangeDeleteCommand extends Command protected $description = 'Delete exchange'; /** - * @param RabbitMQConnector $connector + * @param RabbitMQConnector $connector + * * @throws Exception */ public function handle(RabbitMQConnector $connector): void diff --git a/src/Console/QueueBindCommand.php b/src/Console/QueueBindCommand.php index 686a3430..40799460 100644 --- a/src/Console/QueueBindCommand.php +++ b/src/Console/QueueBindCommand.php @@ -17,7 +17,8 @@ class QueueBindCommand extends Command protected $description = 'Bind queue to exchange'; /** - * @param RabbitMQConnector $connector + * @param RabbitMQConnector $connector + * * @throws Exception */ public function handle(RabbitMQConnector $connector): void diff --git a/src/Console/QueueDeclareCommand.php b/src/Console/QueueDeclareCommand.php index 97742c28..d43f49ce 100644 --- a/src/Console/QueueDeclareCommand.php +++ b/src/Console/QueueDeclareCommand.php @@ -19,7 +19,8 @@ class QueueDeclareCommand extends Command protected $description = 'Declare queue'; /** - * @param RabbitMQConnector $connector + * @param RabbitMQConnector $connector + * * @throws Exception */ public function handle(RabbitMQConnector $connector): void diff --git a/src/Console/QueueDeleteCommand.php b/src/Console/QueueDeleteCommand.php index a1e331e2..8bca7c20 100644 --- a/src/Console/QueueDeleteCommand.php +++ b/src/Console/QueueDeleteCommand.php @@ -17,7 +17,8 @@ class QueueDeleteCommand extends Command protected $description = 'Delete queue'; /** - * @param RabbitMQConnector $connector + * @param RabbitMQConnector $connector + * * @throws Exception */ public function handle(RabbitMQConnector $connector): void diff --git a/src/Console/QueuePurgeCommand.php b/src/Console/QueuePurgeCommand.php index 8b03604a..95b765ea 100644 --- a/src/Console/QueuePurgeCommand.php +++ b/src/Console/QueuePurgeCommand.php @@ -19,7 +19,8 @@ class QueuePurgeCommand extends Command protected $description = 'Purge all messages in queue'; /** - * @param RabbitMQConnector $connector + * @param RabbitMQConnector $connector + * * @throws Exception */ public function handle(RabbitMQConnector $connector): void diff --git a/src/Consumer.php b/src/Consumer.php index e32b3efc..54438097 100644 --- a/src/Consumer.php +++ b/src/Consumer.php @@ -63,10 +63,11 @@ public function setPrefetchCount(int $value): void /** * Listen to the given queue in a loop. * - * @param string $connectionName - * @param string $queue - * @param WorkerOptions $options + * @param string $connectionName + * @param string $queue + * @param WorkerOptions $options * @return int + * * @throws Throwable */ public function daemon($connectionName, $queue, WorkerOptions $options) @@ -147,7 +148,7 @@ function (AMQPMessage $message) use ($connection, $options, $connectionName, $qu $this->exceptions->report($exception); $this->kill(1); - } catch (Exception | Throwable $exception) { + } catch (Exception|Throwable $exception) { $this->exceptions->report($exception); $this->stopWorkerIfLostConnection($exception); @@ -180,9 +181,9 @@ function (AMQPMessage $message) use ($connection, $options, $connectionName, $qu /** * Determine if the daemon should process on this iteration. * - * @param WorkerOptions $options - * @param string $connectionName - * @param string $queue + * @param WorkerOptions $options + * @param string $connectionName + * @param string $queue * @return bool */ protected function daemonShouldRun(WorkerOptions $options, $connectionName, $queue): bool diff --git a/src/Horizon/Listeners/RabbitMQFailedEvent.php b/src/Horizon/Listeners/RabbitMQFailedEvent.php index c034c341..1edfa551 100644 --- a/src/Horizon/Listeners/RabbitMQFailedEvent.php +++ b/src/Horizon/Listeners/RabbitMQFailedEvent.php @@ -19,7 +19,7 @@ class RabbitMQFailedEvent /** * Create a new listener instance. * - * @param Dispatcher $events + * @param Dispatcher $events * @return void */ public function __construct(Dispatcher $events) @@ -30,7 +30,7 @@ public function __construct(Dispatcher $events) /** * Handle the event. * - * @param LaravelJobFailed $event + * @param LaravelJobFailed $event * @return void */ public function handle(LaravelJobFailed $event): void diff --git a/src/Horizon/RabbitMQQueue.php b/src/Horizon/RabbitMQQueue.php index de577897..a4ffdf24 100644 --- a/src/Horizon/RabbitMQQueue.php +++ b/src/Horizon/RabbitMQQueue.php @@ -25,8 +25,9 @@ class RabbitMQQueue extends BaseRabbitMQQueue /** * Get the number of queue jobs that are ready to process. * - * @param string|null $queue + * @param string|null $queue * @return int + * * @throws AMQPProtocolChannelException */ public function readyNow($queue = null): int @@ -93,9 +94,10 @@ public function release($delay, $job, $data, $queue, $attempts = 0) /** * Fire the job deleted event. * - * @param string $queue - * @param RabbitMQJob $job + * @param string $queue + * @param RabbitMQJob $job * @return void + * * @throws BindingResolutionException */ public function deleteReserved($queue, $job): void @@ -106,9 +108,10 @@ public function deleteReserved($queue, $job): void /** * Fire the given event if a dispatcher is bound. * - * @param string $queue - * @param mixed $event + * @param string $queue + * @param mixed $event * @return void + * * @throws BindingResolutionException */ protected function event($queue, $event): void diff --git a/src/Queue/Connectors/RabbitMQConnector.php b/src/Queue/Connectors/RabbitMQConnector.php index a6958082..746a410f 100644 --- a/src/Queue/Connectors/RabbitMQConnector.php +++ b/src/Queue/Connectors/RabbitMQConnector.php @@ -31,9 +31,9 @@ public function __construct(Dispatcher $dispatcher) /** * Establish a queue connection. * - * @param array $config - * + * @param array $config * @return RabbitMQQueue + * * @throws Exception */ public function connect(array $config): Queue @@ -63,8 +63,9 @@ public function connect(array $config): Queue } /** - * @param array $config + * @param array $config * @return AbstractConnection + * * @throws Exception */ protected function createConnection(array $config): AbstractConnection @@ -84,10 +85,10 @@ protected function createConnection(array $config): AbstractConnection /** * Create a queue for the worker. * - * @param string $worker - * @param AbstractConnection $connection - * @param string $queue - * @param array $options + * @param string $worker + * @param AbstractConnection $connection + * @param string $queue + * @param array $options * @return HorizonRabbitMQQueue|RabbitMQQueue|Queue */ protected function createQueue(string $worker, AbstractConnection $connection, string $queue, array $options = []) @@ -105,7 +106,7 @@ protected function createQueue(string $worker, AbstractConnection $connection, s /** * Recursively filter only null values. * - * @param array $array + * @param array $array * @return array */ private function filter(array $array): array diff --git a/src/Queue/Jobs/RabbitMQJob.php b/src/Queue/Jobs/RabbitMQJob.php index 70e9e7f5..4176d80d 100644 --- a/src/Queue/Jobs/RabbitMQJob.php +++ b/src/Queue/Jobs/RabbitMQJob.php @@ -118,7 +118,8 @@ public function delete(): void /** * Release the job back into the queue. * - * @param int $delay + * @param int $delay + * * @throws AMQPProtocolChannelException */ public function release($delay = 0): void diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index 4a684a5d..928979d8 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -82,9 +82,9 @@ class RabbitMQQueue extends Queue implements QueueContract /** * RabbitMQQueue constructor. * - * @param AbstractConnection $connection - * @param string $default - * @param array $options + * @param AbstractConnection $connection + * @param string $default + * @param array $options */ public function __construct( AbstractConnection $connection, @@ -163,9 +163,10 @@ public function later($delay, $job, $data = '', $queue = null) /** * @param $delay * @param $payload - * @param null $queue - * @param int $attempts + * @param null $queue + * @param int $attempts * @return mixed + * * @throws AMQPProtocolChannelException */ public function laterRaw($delay, $payload, $queue = null, $attempts = 0) @@ -204,10 +205,11 @@ public function bulk($jobs, $data = '', $queue = null): void } /** - * @param string $payload - * @param null $queue - * @param array $options + * @param string $payload + * @param null $queue + * @param array $options * @return mixed + * * @throws AMQPProtocolChannelException */ public function bulkRaw(string $payload, $queue = null, array $options = []) @@ -292,6 +294,7 @@ public function getChannel(): AMQPChannel * Job class to use. * * @return string + * * @throws Throwable */ public function getJobClass(): string @@ -310,7 +313,7 @@ public function getJobClass(): string /** * Gets a queue/destination, by default the queue option set on the connection. * - * @param null $queue + * @param null $queue * @return string */ public function getQueue($queue = null): string @@ -322,8 +325,9 @@ public function getQueue($queue = null): string * Checks if the given exchange already present/defined in RabbitMQ. * Returns false when when the exchange is missing. * - * @param string $exchange + * @param string $exchange * @return bool + * * @throws AMQPProtocolChannelException */ public function isExchangeExists(string $exchange): bool @@ -353,11 +357,11 @@ public function isExchangeExists(string $exchange): bool /** * Declare a exchange in rabbitMQ, when not already declared. * - * @param string $name - * @param string $type - * @param bool $durable - * @param bool $autoDelete - * @param array $arguments + * @param string $name + * @param string $type + * @param bool $durable + * @param bool $autoDelete + * @param array $arguments * @return void */ public function declareExchange( @@ -386,9 +390,10 @@ public function declareExchange( /** * Delete a exchange from rabbitMQ, only when present in RabbitMQ. * - * @param string $name - * @param bool $unused + * @param string $name + * @param bool $unused * @return void + * * @throws AMQPProtocolChannelException */ public function deleteExchange(string $name, bool $unused = false): void @@ -410,8 +415,9 @@ public function deleteExchange(string $name, bool $unused = false): void * Checks if the given queue already present/defined in RabbitMQ. * Returns false when when the queue is missing. * - * @param string|null $name + * @param string|null $name * @return bool + * * @throws AMQPProtocolChannelException */ public function isQueueExists(string $name = null): bool @@ -435,10 +441,10 @@ public function isQueueExists(string $name = null): bool /** * Declare a queue in rabbitMQ, when not already declared. * - * @param string $name - * @param bool $durable - * @param bool $autoDelete - * @param array $arguments + * @param string $name + * @param bool $durable + * @param bool $autoDelete + * @param array $arguments * @return void */ public function declareQueue( @@ -465,10 +471,11 @@ public function declareQueue( /** * Delete a queue from rabbitMQ, only when present in RabbitMQ. * - * @param string $name - * @param bool $if_unused - * @param bool $if_empty + * @param string $name + * @param bool $if_unused + * @param bool $if_empty * @return void + * * @throws AMQPProtocolChannelException */ public function deleteQueue(string $name, bool $if_unused = false, bool $if_empty = false): void @@ -483,9 +490,9 @@ public function deleteQueue(string $name, bool $if_unused = false, bool $if_empt /** * Bind a queue to an exchange. * - * @param string $queue - * @param string $exchange - * @param string $routingKey + * @param string $queue + * @param string $exchange + * @param string $routingKey * @return void */ public function bindQueue(string $queue, string $exchange, string $routingKey = ''): void @@ -504,7 +511,7 @@ public function bindQueue(string $queue, string $exchange, string $routingKey = /** * Purge the queue of messages. * - * @param string|null $queue + * @param string|null $queue * @return void */ public function purge(string $queue = null): void @@ -518,7 +525,7 @@ public function purge(string $queue = null): void /** * Acknowledge the message. * - * @param RabbitMQJob $job + * @param RabbitMQJob $job * @return void */ public function ack(RabbitMQJob $job): void @@ -529,9 +536,8 @@ public function ack(RabbitMQJob $job): void /** * Reject the message. * - * @param RabbitMQJob $job - * @param bool $requeue - * + * @param RabbitMQJob $job + * @param bool $requeue * @return void */ public function reject(RabbitMQJob $job, bool $requeue = false): void @@ -543,8 +549,9 @@ public function reject(RabbitMQJob $job, bool $requeue = false): void * Create a AMQP message. * * @param $payload - * @param int $attempts + * @param int $attempts * @return array + * * @throws JsonException */ protected function createMessage($payload, int $attempts = 0): array @@ -587,9 +594,9 @@ protected function createMessage($payload, int $attempts = 0): array /** * Create a payload array from the given job and data. * - * @param string|object $job - * @param string $queue - * @param mixed $data + * @param string|object $job + * @param string $queue + * @param mixed $data * @return array */ protected function createPayloadArray($job, $queue, $data = ''): array @@ -613,6 +620,7 @@ protected function getRandomId(): string * Close the connection to RabbitMQ. * * @return void + * * @throws Exception */ public function close(): void @@ -631,7 +639,7 @@ public function close(): void /** * Get the Queue arguments. * - * @param string $destination + * @param string $destination * @return array */ protected function getQueueArguments(string $destination): array @@ -661,8 +669,8 @@ protected function getQueueArguments(string $destination): array /** * Get the Delay queue arguments. * - * @param string $destination - * @param int $ttl + * @param string $destination + * @param int $ttl * @return array */ protected function getDelayQueueArguments(string $destination, int $ttl): array @@ -691,6 +699,7 @@ protected function isPrioritizeDelayed(): bool * Using more priority layers, will consume more CPU resources and would affect runtimes. * * @see https://www.rabbitmq.com/priority.html + * * @return int */ protected function getQueueMaxPriority(): int @@ -701,7 +710,7 @@ protected function getQueueMaxPriority(): int /** * Get the exchange name, or &null; as default value. * - * @param string|null $exchange + * @param string|null $exchange * @return string|null */ protected function getExchange(string $exchange = null): ?string @@ -713,7 +722,7 @@ protected function getExchange(string $exchange = null): ?string * Get the routing-key for when you use exchanges * The default routing-key is the given destination. * - * @param string $destination + * @param string $destination * @return string */ protected function getRoutingKey(string $destination): string @@ -724,7 +733,7 @@ protected function getRoutingKey(string $destination): string /** * Get the exchangeType, or AMQPExchangeType::DIRECT as default. * - * @param string|null $type + * @param string|null $type * @return string */ protected function getExchangeType(?string $type = null): string @@ -758,7 +767,7 @@ protected function isQuorum(): bool /** * Get the exchange for failed messages. * - * @param string|null $exchange + * @param string|null $exchange * @return string|null */ protected function getFailedExchange(string $exchange = null): ?string @@ -770,7 +779,7 @@ protected function getFailedExchange(string $exchange = null): ?string * Get the routing-key for failed messages * The default routing-key is the given destination substituted by '.failed'. * - * @param string $destination + * @param string $destination * @return string */ protected function getFailedRoutingKey(string $destination): string @@ -781,7 +790,7 @@ protected function getFailedRoutingKey(string $destination): string /** * Checks if the exchange was already declared. * - * @param string $name + * @param string $name * @return bool */ protected function isExchangeDeclared(string $name): bool @@ -792,7 +801,7 @@ protected function isExchangeDeclared(string $name): bool /** * Checks if the queue was already declared. * - * @param string $name + * @param string $name * @return bool */ protected function isQueueDeclared(string $name): bool @@ -803,10 +812,11 @@ protected function isQueueDeclared(string $name): bool /** * Declare the destination when necessary. * - * @param string $destination - * @param string|null $exchange - * @param string|null $exchangeType + * @param string $destination + * @param string|null $exchange + * @param string|null $exchangeType * @return void + * * @throws AMQPProtocolChannelException */ protected function declareDestination( @@ -837,7 +847,7 @@ protected function declareDestination( * Determine all publish properties. * * @param $queue - * @param array $options + * @param array $options * @return array */ protected function publishProperties($queue, array $options = []): array diff --git a/tests/Functional/TestCase.php b/tests/Functional/TestCase.php index ace2c497..40e3e116 100644 --- a/tests/Functional/TestCase.php +++ b/tests/Functional/TestCase.php @@ -162,9 +162,10 @@ protected function getEnvironmentSetUp($app): void /** * @param $object - * @param string $method - * @param array $parameters + * @param string $method + * @param array $parameters * @return mixed + * * @throws Exception */ protected function callMethod($object, string $method, array $parameters = []) From 2401f7c1bcc988b3b694bba160feb824d3306e05 Mon Sep 17 00:00:00 2001 From: vyuldashev Date: Wed, 6 Apr 2022 23:52:10 +0200 Subject: [PATCH 233/375] update changelog --- CHANGELOG-12x.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG-12x.md b/CHANGELOG-12x.md index 750c82a0..3887c2d1 100644 --- a/CHANGELOG-12x.md +++ b/CHANGELOG-12x.md @@ -2,7 +2,11 @@ All notable changes to this project will be documented in this file. -## [Unreleased](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v12.0.0...master) +## [Unreleased](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v12.0.1...master) + +## [12.0.1 (2022-04-06)](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v12.0.0...v12.0.1) + +- Allow laravel to end workers with lost connection [#457](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/457) ## [12.0.0 (2022-02-23)](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v11.4.0...v12.0.0) From 19d4bbe45a1a260c5d5ce734db9aec96b977ac99 Mon Sep 17 00:00:00 2001 From: vyuldashev Date: Thu, 15 Sep 2022 23:03:45 +0200 Subject: [PATCH 234/375] update changelog --- CHANGELOG-13x.md | 9 +++++++++ README.md | 47 +++++++++++++++++++++++++++++------------------ composer.json | 2 +- 3 files changed, 39 insertions(+), 19 deletions(-) create mode 100644 CHANGELOG-13x.md diff --git a/CHANGELOG-13x.md b/CHANGELOG-13x.md new file mode 100644 index 00000000..3198f3c1 --- /dev/null +++ b/CHANGELOG-13x.md @@ -0,0 +1,9 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +## [Unreleased](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v13.0.0...master) + +## [13.0.0 (2022-09-15)](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v12.0.1...v13.0.0) + +- Dispatch a job after DB transaction commit [#468](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/468) diff --git a/README.md b/README.md index aa62b83e..75ffb600 100644 --- a/README.md +++ b/README.md @@ -10,11 +10,9 @@ RabbitMQ Queue driver for Laravel Only the latest version will get new features. Bug fixes will be provided using the following scheme: -| Package Version | Laravel Version | Bug Fixes Until | | -|-----------------|-----------------|---------------------|---------------------------------------------------------------------------------------------| -| 9 | 6 | October 5th, 2021 | [Documentation](https://github.com/vyuldashev/laravel-queue-rabbitmq/blob/v9.0/README.md) | -| 10 | 6, 7 | October 5th, 2021 | [Documentation](https://github.com/vyuldashev/laravel-queue-rabbitmq/blob/v10.0/README.md) | -| 11 | 8 | April 6th, 2021 | [Documentation](https://github.com/vyuldashev/laravel-queue-rabbitmq/blob/master/README.md) | +| Package Version | Laravel Version | Bug Fixes Until | | +|-----------------|-----------------|------------------|---------------------------------------------------------------------------------------------| +| 13 | 9 | August 8th, 2023 | [Documentation](https://github.com/vyuldashev/laravel-queue-rabbitmq/blob/master/README.md) | ## Installation @@ -74,10 +72,11 @@ Add connection to `config/queue.php`: ### Optional Config -Optionally add queue options to the config of a connection. +Optionally add queue options to the config of a connection. Every queue created for this connection, get's the properties. When you want to prioritize messages when they were delayed, then this is possible by adding extra options. + - When max-priority is omitted, the max priority is set with 2 when used. ```php @@ -102,12 +101,13 @@ When you want to prioritize messages when they were delayed, then this is possib ``` When you want to publish messages against an exchange with routing-key's, then this is possible by adding extra options. + - When the exchange is omitted, RabbitMQ will use the `amq.direct` exchange for the routing-key - When routing-key is omitted the routing-key by default is the `queue` name. - When using `%s` in the routing-key the queue_name will be substituted. > Note: when using exchange with routing-key, u probably create your queues with bindings yourself. - + ```php 'connections' => [ // ... @@ -130,14 +130,18 @@ When you want to publish messages against an exchange with routing-key's, then t ], ``` -In Laravel failed jobs are stored into the database. But maybe you want to instruct some other process to also do something with the message. -When you want to instruct RabbitMQ to reroute failed messages to a exchange or a specific queue, then this is possible by adding extra options. +In Laravel failed jobs are stored into the database. But maybe you want to instruct some other process to also do +something with the message. +When you want to instruct RabbitMQ to reroute failed messages to a exchange or a specific queue, then this is possible +by adding extra options. + - When the exchange is omitted, RabbitMQ will use the `amq.direct` exchange for the routing-key - When routing-key is omitted, the routing-key by default the `queue` name is substituted with `'.failed'`. - When using `%s` in the routing-key the queue_name will be substituted. -> Note: When using failed_job exchange with routing-key, u probably need to create your exchange/queue with bindings yourself. - +> Note: When using failed_job exchange with routing-key, u probably need to create your exchange/queue with bindings +> yourself. + ```php 'connections' => [ // ... @@ -161,12 +165,14 @@ When you want to instruct RabbitMQ to reroute failed messages to a exchange or a ``` ### Use your own RabbitMQJob class + Sometimes you have to work with messages published by another application. Those messages probably won't respect Laravel's job payload schema. -The problem with these messages is that, Laravel workers won't be able to determine the actual job or class to execute. +The problem with these messages is that, Laravel workers won't be able to determine the actual job or class to execute. You can extend the build-in `RabbitMQJob::class` and within the queue connection config, you can define your own class. -When you specify an `job` key in the config, with your own class name, every message retrieved from the broker will get wrapped by your own class. +When you specify an `job` key in the config, with your own class name, every message retrieved from the broker will get +wrapped by your own class. An example for the config: @@ -250,11 +256,14 @@ class RabbitMQJob extends BaseJob ## Laravel Usage -Once you completed the configuration you can use Laravel Queue API. If you used other queue drivers you do not need to change anything else. If you do not know how to use Queue API, please refer to the official Laravel documentation: http://laravel.com/docs/queues +Once you completed the configuration you can use Laravel Queue API. If you used other queue drivers you do not need to +change anything else. If you do not know how to use Queue API, please refer to the official Laravel +documentation: http://laravel.com/docs/queues ## Laravel Horizon Usage -Starting with 8.0, this package supports [Laravel Horizon](http://horizon.laravel.com) out of the box. Firstly, install Horizon and then set `RABBITMQ_WORKER` to `horizon`. +Starting with 8.0, this package supports [Laravel Horizon](http://horizon.laravel.com) out of the box. Firstly, install +Horizon and then set `RABBITMQ_WORKER` to `horizon`. ## Lumen Usage @@ -266,11 +275,12 @@ $app->register(VladimirYuldashev\LaravelQueueRabbitMQ\LaravelQueueRabbitMQServic ## Consuming Messages -There are two ways of consuming messages. +There are two ways of consuming messages. 1. `queue:work` command which is Laravel's built-in command. This command utilizes `basic_get`. -2. `rabbitmq:consume` command which is provided by this package. This command utilizes `basic_consume` and is more performant than `basic_get` by ~2x. +2. `rabbitmq:consume` command which is provided by this package. This command utilizes `basic_consume` and is more + performant than `basic_get` by ~2x. ## Testing @@ -302,4 +312,5 @@ composer fix:style ## Contribution -You can contribute to this package by discovering bugs and opening issues. Please, add to which version of package you create pull request or issue. (e.g. [5.2] Fatal error on delayed job) +You can contribute to this package by discovering bugs and opening issues. Please, add to which version of package you +create pull request or issue. (e.g. [5.2] Fatal error on delayed job) diff --git a/composer.json b/composer.json index 3873bfbf..2b8b6989 100644 --- a/composer.json +++ b/composer.json @@ -33,7 +33,7 @@ }, "extra": { "branch-alias": { - "dev-master": "11.0-dev" + "dev-master": "13.0-dev" }, "laravel": { "providers": [ From 842ce70fffbac7db3cb9228a8697eff4782487d1 Mon Sep 17 00:00:00 2001 From: Shahram Date: Fri, 16 Sep 2022 01:36:04 +0430 Subject: [PATCH 235/375] Dispatch a job after DB transaction commit (#468) --- src/Queue/Connectors/RabbitMQConnector.php | 13 +++++++--- src/Queue/RabbitMQQueue.php | 23 ++++++++++++++---- tests/Feature/TestCase.php | 28 ++++++++++++++++++++++ tests/Mocks/TestJob.php | 4 ++++ 4 files changed, 61 insertions(+), 7 deletions(-) diff --git a/src/Queue/Connectors/RabbitMQConnector.php b/src/Queue/Connectors/RabbitMQConnector.php index 746a410f..ca8189ca 100644 --- a/src/Queue/Connectors/RabbitMQConnector.php +++ b/src/Queue/Connectors/RabbitMQConnector.php @@ -44,6 +44,7 @@ public function connect(array $config): Queue Arr::get($config, 'worker', 'default'), $connection, $config['queue'], + $config['after_commit'] ?? null, Arr::get($config, 'options.queue', []) ); @@ -88,14 +89,20 @@ protected function createConnection(array $config): AbstractConnection * @param string $worker * @param AbstractConnection $connection * @param string $queue + * @param bool $dispatchAfterCommit * @param array $options * @return HorizonRabbitMQQueue|RabbitMQQueue|Queue */ - protected function createQueue(string $worker, AbstractConnection $connection, string $queue, array $options = []) - { + protected function createQueue( + string $worker, + AbstractConnection $connection, + string $queue, + $dispatchAfterCommit, + array $options = [] + ) { switch ($worker) { case 'default': - return new RabbitMQQueue($connection, $queue, $options); + return new RabbitMQQueue($connection, $queue, $dispatchAfterCommit, $options); case 'horizon': return new HorizonRabbitMQQueue($connection, $queue, $options); default: diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index 928979d8..8bf32539 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -84,17 +84,20 @@ class RabbitMQQueue extends Queue implements QueueContract * * @param AbstractConnection $connection * @param string $default + * @param bool $dispatchAfterCommit * @param array $options */ public function __construct( AbstractConnection $connection, string $default, + $dispatchAfterCommit = false, array $options = [] ) { $this->connection = $connection; $this->channel = $connection->channel(); $this->default = $default; $this->options = $options; + $this->dispatchAfterCommit = $dispatchAfterCommit; } /** @@ -125,7 +128,15 @@ public function size($queue = null): int */ public function push($job, $data = '', $queue = null) { - return $this->pushRaw($this->createPayload($job, $queue, $data), $queue, []); + return $this->enqueueUsing( + $job, + $this->createPayload($job, $this->getQueue($queue), $data), + $queue, + null, + function ($payload, $queue) { + return $this->pushRaw($payload, $queue); + } + ); } /** @@ -153,10 +164,14 @@ public function pushRaw($payload, $queue = null, array $options = []) */ public function later($delay, $job, $data = '', $queue = null) { - return $this->laterRaw( + return $this->enqueueUsing( + $job, + $this->createPayload($job, $this->getQueue($queue), $data), + $queue, $delay, - $this->createPayload($job, $queue, $data), - $queue + function ($payload, $queue, $delay) { + return $this->laterRaw($delay, $payload, $queue); + } ); } diff --git a/tests/Feature/TestCase.php b/tests/Feature/TestCase.php index 0a387ef6..299e7024 100644 --- a/tests/Feature/TestCase.php +++ b/tests/Feature/TestCase.php @@ -2,6 +2,7 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Feature; +use Illuminate\Database\DatabaseTransactionsManager; use Illuminate\Support\Facades\Queue; use Illuminate\Support\Str; use PhpAmqpLib\Exception\AMQPProtocolChannelException; @@ -93,6 +94,33 @@ public function testPush(): void $this->assertSame(0, Queue::size()); } + public function testPushAfterCommit(): void + { + $transaction = new DatabaseTransactionsManager; + + $this->app->singleton('db.transactions', function ($app) use ($transaction) { + $transaction->begin('FakeDBConnection', 1); + + return $transaction; + }); + + TestJob::dispatch()->afterCommit(); + + sleep(1); + $this->assertSame(0, Queue::size()); + $this->assertNull(Queue::pop()); + + $transaction->commit('FakeDBConnection'); + + sleep(1); + + $this->assertSame(1, Queue::size()); + $this->assertNotNull($job = Queue::pop()); + + $job->delete(); + $this->assertSame(0, Queue::size()); + } + public function testLaterRaw(): void { $payload = Str::random(); diff --git a/tests/Mocks/TestJob.php b/tests/Mocks/TestJob.php index 0f290fd4..f97c7e12 100644 --- a/tests/Mocks/TestJob.php +++ b/tests/Mocks/TestJob.php @@ -2,10 +2,14 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Mocks; +use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; +use Illuminate\Foundation\Bus\Dispatchable; class TestJob implements ShouldQueue { + use Dispatchable, Queueable; + public $i; public function __construct($i = 0) From 5620e46e1713d7272fe01745c840ffeadce9eeea Mon Sep 17 00:00:00 2001 From: vyuldashev Date: Thu, 15 Sep 2022 23:10:27 +0200 Subject: [PATCH 236/375] install laravel pint, small improvements --- .github/workflows/tests.yml | 5 +- .php-cs-fixer.dist.php | 57 ---------------------- .styleci.yml | 2 - README.md | 2 +- composer.json | 4 +- src/Queue/Connectors/RabbitMQConnector.php | 4 +- src/Queue/RabbitMQQueue.php | 2 +- 7 files changed, 8 insertions(+), 68 deletions(-) delete mode 100644 .php-cs-fixer.dist.php delete mode 100644 .styleci.yml diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 059be9ee..702bfe9b 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -42,9 +42,8 @@ jobs: - name: Install dependencies run: composer update --${{ matrix.stability }} --prefer-dist --no-interaction --no-progress - - name: Run PHP CS Fixer - if: ${{ matrix.php != 8.0 }} - run: vendor/bin/php-cs-fixer fix --config=.php-cs-fixer.dist.php --allow-risky=yes --dry-run --diff --verbose + - name: Run Laravel Pint + run: ./vendor/bin/pint --test - name: Execute tests run: sleep 10 && vendor/bin/phpunit --verbose diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php deleted file mode 100644 index b1a2c0e9..00000000 --- a/.php-cs-fixer.dist.php +++ /dev/null @@ -1,57 +0,0 @@ -in(['config', 'src', 'tests']); - -return (new PhpCsFixer\Config()) - ->setUsingCache(false) - ->setFinder($finder) - ->setRules([ - 'psr_autoloading' => false, - '@PSR2' => true, - 'blank_line_after_namespace' => true, - 'braces' => true, - 'class_definition' => true, - 'concat_space' => ['spacing' => 'none'], - 'elseif' => true, - 'function_declaration' => true, - 'indentation_type' => true, - 'line_ending' => true, - 'constant_case' => true, - 'lowercase_keywords' => true, - 'method_argument_space' => [ - 'on_multiline' => 'ensure_fully_multiline', - ], - 'no_break_comment' => true, - 'no_closing_tag' => true, - 'no_spaces_after_function_name' => true, - 'no_spaces_inside_parenthesis' => true, - 'no_trailing_whitespace' => true, - 'no_trailing_whitespace_in_comment' => true, - 'single_blank_line_at_eof' => true, - 'single_class_element_per_statement' => [ - 'elements' => ['property'], - ], - 'single_import_per_statement' => true, - 'single_line_after_imports' => true, - 'switch_case_semicolon_to_colon' => true, - 'switch_case_space' => true, - 'visibility_required' => true, - 'encoding' => true, - 'full_opening_tag' => true, - 'blank_line_before_statement' => true, - 'no_trailing_comma_in_singleline_array' => true, - 'trailing_comma_in_multiline' => true, - 'array_indentation' => true, - 'binary_operator_spaces' => [ - 'operators' => [ - '=' => 'single_space', - ], - ], - 'fully_qualified_strict_types' => true, - 'void_return' => true, - 'cast_spaces' => [ - 'space' => 'single', - ], - 'not_operator_with_successor_space' => true, - ]); diff --git a/.styleci.yml b/.styleci.yml deleted file mode 100644 index fe42f6da..00000000 --- a/.styleci.yml +++ /dev/null @@ -1,2 +0,0 @@ -preset: laravel - diff --git a/README.md b/README.md index 75ffb600..64281dfe 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,7 @@ Add connection to `config/queue.php`: * Set to "horizon" if you wish to use Laravel Horizon. */ 'worker' => env('RABBITMQ_WORKER', 'default'), - + 'after_commit' => false, ], // ... diff --git a/composer.json b/composer.json index 2b8b6989..15bf6e85 100644 --- a/composer.json +++ b/composer.json @@ -18,8 +18,8 @@ "phpunit/phpunit": "^9.3", "mockery/mockery": "^1.0", "laravel/horizon": "^5.0", - "friendsofphp/php-cs-fixer": "^3.6", - "orchestra/testbench": "^7.0" + "orchestra/testbench": "^7.0", + "laravel/pint": "^1.2" }, "autoload": { "psr-4": { diff --git a/src/Queue/Connectors/RabbitMQConnector.php b/src/Queue/Connectors/RabbitMQConnector.php index ca8189ca..0598276b 100644 --- a/src/Queue/Connectors/RabbitMQConnector.php +++ b/src/Queue/Connectors/RabbitMQConnector.php @@ -44,7 +44,7 @@ public function connect(array $config): Queue Arr::get($config, 'worker', 'default'), $connection, $config['queue'], - $config['after_commit'] ?? null, + Arr::get($config, 'after_commit', false), Arr::get($config, 'options.queue', []) ); @@ -97,7 +97,7 @@ protected function createQueue( string $worker, AbstractConnection $connection, string $queue, - $dispatchAfterCommit, + bool $dispatchAfterCommit, array $options = [] ) { switch ($worker) { diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index 8bf32539..b5916bd2 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -90,7 +90,7 @@ class RabbitMQQueue extends Queue implements QueueContract public function __construct( AbstractConnection $connection, string $default, - $dispatchAfterCommit = false, + bool $dispatchAfterCommit = false, array $options = [] ) { $this->connection = $connection; From 3821b0c330d54e90067282e3295a5c1a392ecfb1 Mon Sep 17 00:00:00 2001 From: Ben Periton <104396+benperiton@users.noreply.github.com> Date: Fri, 16 Sep 2022 22:38:16 +0100 Subject: [PATCH 237/375] Add $dispatchAfterCommit when running via Horizon (#484) Queue won't start when running with Horizon as the arguments are wrong --- src/Queue/Connectors/RabbitMQConnector.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Queue/Connectors/RabbitMQConnector.php b/src/Queue/Connectors/RabbitMQConnector.php index 0598276b..903e1d3a 100644 --- a/src/Queue/Connectors/RabbitMQConnector.php +++ b/src/Queue/Connectors/RabbitMQConnector.php @@ -104,7 +104,7 @@ protected function createQueue( case 'default': return new RabbitMQQueue($connection, $queue, $dispatchAfterCommit, $options); case 'horizon': - return new HorizonRabbitMQQueue($connection, $queue, $options); + return new HorizonRabbitMQQueue($connection, $queue, $dispatchAfterCommit, $options); default: return new $worker($connection, $queue, $options); } From e15f988004f3d8b4f65971042a5f9b29fd5b5876 Mon Sep 17 00:00:00 2001 From: vyuldashev Date: Fri, 16 Sep 2022 23:38:37 +0200 Subject: [PATCH 238/375] v13.0.1 --- CHANGELOG-13x.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG-13x.md b/CHANGELOG-13x.md index 3198f3c1..a280ebfc 100644 --- a/CHANGELOG-13x.md +++ b/CHANGELOG-13x.md @@ -2,7 +2,11 @@ All notable changes to this project will be documented in this file. -## [Unreleased](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v13.0.0...master) +## [Unreleased](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v13.0.1...master) + +## [13.0.1 (2022-09-16)](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v13.0.0...v13.0.1) + +- Add $dispatchAfterCommit when running via Horizon [#484](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/484) ## [13.0.0 (2022-09-15)](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v12.0.1...v13.0.0) From 9bd02dfe8bb431485e67005e9ad66b3e45e81d9f Mon Sep 17 00:00:00 2001 From: ShuQingZai Date: Sat, 10 Dec 2022 18:34:35 +0800 Subject: [PATCH 239/375] Fix local #501 - laravel 9.41 --- src/Horizon/RabbitMQQueue.php | 2 +- src/Queue/RabbitMQQueue.php | 15 +++++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/Horizon/RabbitMQQueue.php b/src/Horizon/RabbitMQQueue.php index a4ffdf24..bf5b2ee2 100644 --- a/src/Horizon/RabbitMQQueue.php +++ b/src/Horizon/RabbitMQQueue.php @@ -64,7 +64,7 @@ public function later($delay, $job, $data = '', $queue = null) { $payload = (new JobPayload($this->createPayload($job, $data)))->prepare($job)->value; - return tap(parent::pushRaw($payload, $queue, ['delay' => $this->secondsUntil($delay)]), function () use ($payload, $queue): void { + return tap(parent::laterRaw($delay, $payload, $queue), function () use ($payload, $queue): void { $this->event($this->getQueue($queue), new JobPushed($payload)); }); } diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index b5916bd2..adcac0db 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -188,11 +188,18 @@ public function laterRaw($delay, $payload, $queue = null, $attempts = 0) { $ttl = $this->secondsUntil($delay) * 1000; + // default options + $options = ['delay' => $delay, 'attempts' => $attempts]; + // When no ttl just publish a new message to the exchange or queue if ($ttl <= 0) { - return $this->pushRaw($payload, $queue, ['delay' => $delay, 'attempts' => $attempts]); + return $this->pushRaw($payload, $queue, $options); } + // Create a main queue to handle delayed messages + [$mainDestination, $exchange, $exchangeType, $attempts] = $this->publishProperties($queue, $options); + $this->declareDestination($mainDestination, $exchange, $exchangeType); + $destination = $this->getQueue($queue).'.delay.'.$ttl; $this->declareQueue($destination, true, false, $this->getDelayQueueArguments($this->getQueue($queue), $ttl)); @@ -754,9 +761,9 @@ protected function getRoutingKey(string $destination): string protected function getExchangeType(?string $type = null): string { return @constant(AMQPExchangeType::class.'::'.Str::upper($type ?: Arr::get( - $this->options, - 'exchange_type' - ) ?: 'direct')) ?: AMQPExchangeType::DIRECT; + $this->options, + 'exchange_type' + ) ?: 'direct')) ?: AMQPExchangeType::DIRECT; } /** From e462452947b6670a4eb352d25d785c4ce13aee76 Mon Sep 17 00:00:00 2001 From: ShuQingZai Date: Sat, 10 Dec 2022 19:53:44 +0800 Subject: [PATCH 240/375] Fix local #499 - laravel 9.42 --- src/Consumer.php | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/Consumer.php b/src/Consumer.php index 54438097..ffdaf47d 100644 --- a/src/Consumer.php +++ b/src/Consumer.php @@ -88,7 +88,7 @@ public function daemon($connectionName, $queue, WorkerOptions $options) $this->channel->basic_qos( $this->prefetchSize, $this->prefetchCount, - null + false ); $jobClass = $connection->getJobClass(); @@ -147,7 +147,7 @@ function (AMQPMessage $message) use ($connection, $options, $connectionName, $qu } catch (AMQPRuntimeException $exception) { $this->exceptions->report($exception); - $this->kill(1); + $this->kill(self::EXIT_ERROR, $options); } catch (Exception|Throwable $exception) { $this->exceptions->report($exception); @@ -171,7 +171,7 @@ function (AMQPMessage $message) use ($connection, $options, $connectionName, $qu ); if (! is_null($status)) { - return $this->stop($status); + return $this->stop($status, $options); } $this->currentJob = null; @@ -194,15 +194,16 @@ protected function daemonShouldRun(WorkerOptions $options, $connectionName, $que /** * Stop listening and bail out of the script. * - * @param int $status + * @param int $status + * @param WorkerOptions|null $options * @return int */ - public function stop($status = 0): int + public function stop($status = 0, $options = null) { // Tell the server you are going to stop consuming. // It will finish up the last message and not send you any more. $this->channel->basic_cancel($this->consumerTag, false, true); - return parent::stop($status); + return parent::stop($status, $options); } } From 87bf82e0dd2a5ce7ccd2d6f09249b501c5854e88 Mon Sep 17 00:00:00 2001 From: ShuQingZai Date: Wed, 4 Jan 2023 09:54:24 +0800 Subject: [PATCH 241/375] code style #449 #499 #501 --- src/Consumer.php | 2 +- src/Queue/RabbitMQQueue.php | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Consumer.php b/src/Consumer.php index ffdaf47d..d27b96c4 100644 --- a/src/Consumer.php +++ b/src/Consumer.php @@ -194,7 +194,7 @@ protected function daemonShouldRun(WorkerOptions $options, $connectionName, $que /** * Stop listening and bail out of the script. * - * @param int $status + * @param int $status * @param WorkerOptions|null $options * @return int */ diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index adcac0db..38ad1e94 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -761,9 +761,9 @@ protected function getRoutingKey(string $destination): string protected function getExchangeType(?string $type = null): string { return @constant(AMQPExchangeType::class.'::'.Str::upper($type ?: Arr::get( - $this->options, - 'exchange_type' - ) ?: 'direct')) ?: AMQPExchangeType::DIRECT; + $this->options, + 'exchange_type' + ) ?: 'direct')) ?: AMQPExchangeType::DIRECT; } /** From a092f35f06a76babcc7ffbf79996d3ad427b65a8 Mon Sep 17 00:00:00 2001 From: Melchior Jonathan Kokernoot Date: Wed, 11 Jan 2023 13:48:31 +0100 Subject: [PATCH 242/375] Small improvements to readme --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 64281dfe..24c25862 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,7 @@ Add connection to `config/queue.php`: ### Optional Config Optionally add queue options to the config of a connection. -Every queue created for this connection, get's the properties. +Every queue created for this connection, gets the properties. When you want to prioritize messages when they were delayed, then this is possible by adding extra options. @@ -100,13 +100,13 @@ When you want to prioritize messages when they were delayed, then this is possib ], ``` -When you want to publish messages against an exchange with routing-key's, then this is possible by adding extra options. +When you want to publish messages against an exchange with routing-keys, then this is possible by adding extra options. - When the exchange is omitted, RabbitMQ will use the `amq.direct` exchange for the routing-key - When routing-key is omitted the routing-key by default is the `queue` name. - When using `%s` in the routing-key the queue_name will be substituted. -> Note: when using exchange with routing-key, u probably create your queues with bindings yourself. +> Note: when using an exchange with routing-key, you probably create your queues with bindings yourself. ```php 'connections' => [ @@ -139,7 +139,7 @@ by adding extra options. - When routing-key is omitted, the routing-key by default the `queue` name is substituted with `'.failed'`. - When using `%s` in the routing-key the queue_name will be substituted. -> Note: When using failed_job exchange with routing-key, u probably need to create your exchange/queue with bindings +> Note: When using failed_job exchange with routing-key, you probably need to create your exchange/queue with bindings > yourself. ```php @@ -171,7 +171,7 @@ Those messages probably won't respect Laravel's job payload schema. The problem with these messages is that, Laravel workers won't be able to determine the actual job or class to execute. You can extend the build-in `RabbitMQJob::class` and within the queue connection config, you can define your own class. -When you specify an `job` key in the config, with your own class name, every message retrieved from the broker will get +When you specify a `job` key in the config, with your own class name, every message retrieved from the broker will get wrapped by your own class. An example for the config: @@ -256,8 +256,8 @@ class RabbitMQJob extends BaseJob ## Laravel Usage -Once you completed the configuration you can use Laravel Queue API. If you used other queue drivers you do not need to -change anything else. If you do not know how to use Queue API, please refer to the official Laravel +Once you completed the configuration you can use the Laravel Queue API. If you used other queue drivers you do not need to +change anything else. If you do not know how to use the Queue API, please refer to the official Laravel documentation: http://laravel.com/docs/queues ## Laravel Horizon Usage From 7c82950b5732cf081c3a83806bea2a79b8183b22 Mon Sep 17 00:00:00 2001 From: Matthew Porter Date: Wed, 25 Jan 2023 11:41:08 -0700 Subject: [PATCH 243/375] v13.1.0 --- CHANGELOG-13x.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG-13x.md b/CHANGELOG-13x.md index a280ebfc..f984d2cc 100644 --- a/CHANGELOG-13x.md +++ b/CHANGELOG-13x.md @@ -2,7 +2,13 @@ All notable changes to this project will be documented in this file. -## [Unreleased](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v13.0.1...master) +## [Unreleased](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v13.1.0...master) + +## [31.1.0] (2023-01-25)(https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v13.0.1...v13.1.0) + +- Fix delay parameter not being used [#502](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/502) +- Resolve Laravel 9 incompatabilities [#502](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/502) +- Fix Horizon invalid delay property [#502](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/502) ## [13.0.1 (2022-09-16)](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v13.0.0...v13.0.1) From 1adc8958e65f1820b340c8e1397d1480e1980f6c Mon Sep 17 00:00:00 2001 From: Matthew Porter Date: Wed, 25 Jan 2023 11:45:00 -0700 Subject: [PATCH 244/375] Fix changelog --- CHANGELOG-13x.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG-13x.md b/CHANGELOG-13x.md index f984d2cc..0f9e7678 100644 --- a/CHANGELOG-13x.md +++ b/CHANGELOG-13x.md @@ -4,7 +4,7 @@ All notable changes to this project will be documented in this file. ## [Unreleased](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v13.1.0...master) -## [31.1.0] (2023-01-25)(https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v13.0.1...v13.1.0) +## [13.1.0 (2023-01-25)](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v13.0.1...v13.1.0) - Fix delay parameter not being used [#502](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/502) - Resolve Laravel 9 incompatabilities [#502](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/502) From 151d9527c271d544fbdc3e81ff604d381a31276d Mon Sep 17 00:00:00 2001 From: Morten Hauberg-Lund Date: Thu, 26 Jan 2023 06:29:03 +0100 Subject: [PATCH 245/375] Use `queue:work` for multi queue consuming --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9d889808..df0ac4ff 100644 --- a/README.md +++ b/README.md @@ -268,7 +268,7 @@ $app->register(VladimirYuldashev\LaravelQueueRabbitMQ\LaravelQueueRabbitMQServic There are two ways of consuming messages. -1. `queue:work` command which is Laravel's built-in command. This command utilizes `basic_get`. +1. `queue:work` command which is Laravel's built-in command. This command utilizes `basic_get`. Use this if you want to consume multiple queues. 2. `rabbitmq:consume` command which is provided by this package. This command utilizes `basic_consume` and is more performant than `basic_get` by ~2x, but does not support multiple queues. From 5bc3a526c15e758cedd56f19a52874b5efb40d3a Mon Sep 17 00:00:00 2001 From: Seb Date: Tue, 21 Feb 2023 12:57:06 -0800 Subject: [PATCH 246/375] Enable PHP 8.2 in tests --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 702bfe9b..5633c682 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -13,7 +13,7 @@ jobs: strategy: fail-fast: true matrix: - php: ['8.0', '8.1'] + php: ['8.0', '8.1', '8.2'] stability: [prefer-lowest, prefer-stable] name: PHP ${{ matrix.php }} - ${{ matrix.stability }} From eb397a0015c8657b7a37e8d4fe79cff9b5f0e3aa Mon Sep 17 00:00:00 2001 From: Seb Date: Tue, 21 Feb 2023 12:57:42 -0800 Subject: [PATCH 247/375] fix pint linter issues --- src/Console/ExchangeDeclareCommand.php | 2 - src/Console/ExchangeDeleteCommand.php | 2 - src/Console/QueueBindCommand.php | 2 - src/Console/QueueDeclareCommand.php | 2 - src/Console/QueueDeleteCommand.php | 2 - src/Console/QueuePurgeCommand.php | 2 - src/Consumer.php | 3 - src/Horizon/Listeners/RabbitMQFailedEvent.php | 4 - src/Horizon/RabbitMQQueue.php | 3 - src/LaravelQueueRabbitMQServiceProvider.php | 4 - src/Queue/Connectors/RabbitMQConnector.php | 12 -- src/Queue/Jobs/RabbitMQJob.php | 6 - src/Queue/RabbitMQQueue.php | 106 ------------------ tests/Functional/TestCase.php | 3 - 14 files changed, 153 deletions(-) diff --git a/src/Console/ExchangeDeclareCommand.php b/src/Console/ExchangeDeclareCommand.php index 99f0b753..43aa9c5c 100644 --- a/src/Console/ExchangeDeclareCommand.php +++ b/src/Console/ExchangeDeclareCommand.php @@ -18,8 +18,6 @@ class ExchangeDeclareCommand extends Command protected $description = 'Declare exchange'; /** - * @param RabbitMQConnector $connector - * * @throws Exception */ public function handle(RabbitMQConnector $connector): void diff --git a/src/Console/ExchangeDeleteCommand.php b/src/Console/ExchangeDeleteCommand.php index 904f38ba..d5a8d8d4 100644 --- a/src/Console/ExchangeDeleteCommand.php +++ b/src/Console/ExchangeDeleteCommand.php @@ -16,8 +16,6 @@ class ExchangeDeleteCommand extends Command protected $description = 'Delete exchange'; /** - * @param RabbitMQConnector $connector - * * @throws Exception */ public function handle(RabbitMQConnector $connector): void diff --git a/src/Console/QueueBindCommand.php b/src/Console/QueueBindCommand.php index 40799460..ccbbd706 100644 --- a/src/Console/QueueBindCommand.php +++ b/src/Console/QueueBindCommand.php @@ -17,8 +17,6 @@ class QueueBindCommand extends Command protected $description = 'Bind queue to exchange'; /** - * @param RabbitMQConnector $connector - * * @throws Exception */ public function handle(RabbitMQConnector $connector): void diff --git a/src/Console/QueueDeclareCommand.php b/src/Console/QueueDeclareCommand.php index d43f49ce..54d6ea32 100644 --- a/src/Console/QueueDeclareCommand.php +++ b/src/Console/QueueDeclareCommand.php @@ -19,8 +19,6 @@ class QueueDeclareCommand extends Command protected $description = 'Declare queue'; /** - * @param RabbitMQConnector $connector - * * @throws Exception */ public function handle(RabbitMQConnector $connector): void diff --git a/src/Console/QueueDeleteCommand.php b/src/Console/QueueDeleteCommand.php index 8bca7c20..b2586ecd 100644 --- a/src/Console/QueueDeleteCommand.php +++ b/src/Console/QueueDeleteCommand.php @@ -17,8 +17,6 @@ class QueueDeleteCommand extends Command protected $description = 'Delete queue'; /** - * @param RabbitMQConnector $connector - * * @throws Exception */ public function handle(RabbitMQConnector $connector): void diff --git a/src/Console/QueuePurgeCommand.php b/src/Console/QueuePurgeCommand.php index 95b765ea..49be839b 100644 --- a/src/Console/QueuePurgeCommand.php +++ b/src/Console/QueuePurgeCommand.php @@ -19,8 +19,6 @@ class QueuePurgeCommand extends Command protected $description = 'Purge all messages in queue'; /** - * @param RabbitMQConnector $connector - * * @throws Exception */ public function handle(RabbitMQConnector $connector): void diff --git a/src/Consumer.php b/src/Consumer.php index d27b96c4..bd7f099d 100644 --- a/src/Consumer.php +++ b/src/Consumer.php @@ -65,7 +65,6 @@ public function setPrefetchCount(int $value): void * * @param string $connectionName * @param string $queue - * @param WorkerOptions $options * @return int * * @throws Throwable @@ -181,10 +180,8 @@ function (AMQPMessage $message) use ($connection, $options, $connectionName, $qu /** * Determine if the daemon should process on this iteration. * - * @param WorkerOptions $options * @param string $connectionName * @param string $queue - * @return bool */ protected function daemonShouldRun(WorkerOptions $options, $connectionName, $queue): bool { diff --git a/src/Horizon/Listeners/RabbitMQFailedEvent.php b/src/Horizon/Listeners/RabbitMQFailedEvent.php index 1edfa551..00674827 100644 --- a/src/Horizon/Listeners/RabbitMQFailedEvent.php +++ b/src/Horizon/Listeners/RabbitMQFailedEvent.php @@ -19,7 +19,6 @@ class RabbitMQFailedEvent /** * Create a new listener instance. * - * @param Dispatcher $events * @return void */ public function __construct(Dispatcher $events) @@ -29,9 +28,6 @@ public function __construct(Dispatcher $events) /** * Handle the event. - * - * @param LaravelJobFailed $event - * @return void */ public function handle(LaravelJobFailed $event): void { diff --git a/src/Horizon/RabbitMQQueue.php b/src/Horizon/RabbitMQQueue.php index bf5b2ee2..7b038003 100644 --- a/src/Horizon/RabbitMQQueue.php +++ b/src/Horizon/RabbitMQQueue.php @@ -26,7 +26,6 @@ class RabbitMQQueue extends BaseRabbitMQQueue * Get the number of queue jobs that are ready to process. * * @param string|null $queue - * @return int * * @throws AMQPProtocolChannelException */ @@ -96,7 +95,6 @@ public function release($delay, $job, $data, $queue, $attempts = 0) * * @param string $queue * @param RabbitMQJob $job - * @return void * * @throws BindingResolutionException */ @@ -110,7 +108,6 @@ public function deleteReserved($queue, $job): void * * @param string $queue * @param mixed $event - * @return void * * @throws BindingResolutionException */ diff --git a/src/LaravelQueueRabbitMQServiceProvider.php b/src/LaravelQueueRabbitMQServiceProvider.php index 3be0fd81..ee46d6cd 100644 --- a/src/LaravelQueueRabbitMQServiceProvider.php +++ b/src/LaravelQueueRabbitMQServiceProvider.php @@ -12,8 +12,6 @@ class LaravelQueueRabbitMQServiceProvider extends ServiceProvider { /** * Register the service provider. - * - * @return void */ public function register(): void { @@ -60,8 +58,6 @@ public function register(): void /** * Register the application's event listeners. - * - * @return void */ public function boot(): void { diff --git a/src/Queue/Connectors/RabbitMQConnector.php b/src/Queue/Connectors/RabbitMQConnector.php index 903e1d3a..9fc8615f 100644 --- a/src/Queue/Connectors/RabbitMQConnector.php +++ b/src/Queue/Connectors/RabbitMQConnector.php @@ -31,7 +31,6 @@ public function __construct(Dispatcher $dispatcher) /** * Establish a queue connection. * - * @param array $config * @return RabbitMQQueue * * @throws Exception @@ -64,9 +63,6 @@ public function connect(array $config): Queue } /** - * @param array $config - * @return AbstractConnection - * * @throws Exception */ protected function createConnection(array $config): AbstractConnection @@ -86,11 +82,6 @@ protected function createConnection(array $config): AbstractConnection /** * Create a queue for the worker. * - * @param string $worker - * @param AbstractConnection $connection - * @param string $queue - * @param bool $dispatchAfterCommit - * @param array $options * @return HorizonRabbitMQQueue|RabbitMQQueue|Queue */ protected function createQueue( @@ -112,9 +103,6 @@ protected function createQueue( /** * Recursively filter only null values. - * - * @param array $array - * @return array */ private function filter(array $array): array { diff --git a/src/Queue/Jobs/RabbitMQJob.php b/src/Queue/Jobs/RabbitMQJob.php index 4176d80d..abcdfab4 100644 --- a/src/Queue/Jobs/RabbitMQJob.php +++ b/src/Queue/Jobs/RabbitMQJob.php @@ -137,8 +137,6 @@ public function release($delay = 0): void /** * Get the underlying RabbitMQ connection. - * - * @return RabbitMQQueue */ public function getRabbitMQ(): RabbitMQQueue { @@ -147,8 +145,6 @@ public function getRabbitMQ(): RabbitMQQueue /** * Get the underlying RabbitMQ message. - * - * @return AMQPMessage */ public function getRabbitMQMessage(): AMQPMessage { @@ -157,8 +153,6 @@ public function getRabbitMQMessage(): AMQPMessage /** * Get the headers from the rabbitMQ message. - * - * @return array|null */ protected function getRabbitMQMessageHeaders(): ?array { diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index 38ad1e94..030a4734 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -81,11 +81,6 @@ class RabbitMQQueue extends Queue implements QueueContract /** * RabbitMQQueue constructor. - * - * @param AbstractConnection $connection - * @param string $default - * @param bool $dispatchAfterCommit - * @param array $options */ public function __construct( AbstractConnection $connection, @@ -176,8 +171,6 @@ function ($payload, $queue, $delay) { } /** - * @param $delay - * @param $payload * @param null $queue * @param int $attempts * @return mixed @@ -227,9 +220,7 @@ public function bulk($jobs, $data = '', $queue = null): void } /** - * @param string $payload * @param null $queue - * @param array $options * @return mixed * * @throws AMQPProtocolChannelException @@ -296,17 +287,11 @@ public function pop($queue = null) return null; } - /** - * @return AbstractConnection - */ public function getConnection(): AbstractConnection { return $this->connection; } - /** - * @return AMQPChannel - */ public function getChannel(): AMQPChannel { return $this->channel; @@ -315,7 +300,6 @@ public function getChannel(): AMQPChannel /** * Job class to use. * - * @return string * * @throws Throwable */ @@ -336,7 +320,6 @@ public function getJobClass(): string * Gets a queue/destination, by default the queue option set on the connection. * * @param null $queue - * @return string */ public function getQueue($queue = null): string { @@ -347,8 +330,6 @@ public function getQueue($queue = null): string * Checks if the given exchange already present/defined in RabbitMQ. * Returns false when when the exchange is missing. * - * @param string $exchange - * @return bool * * @throws AMQPProtocolChannelException */ @@ -378,13 +359,6 @@ public function isExchangeExists(string $exchange): bool /** * Declare a exchange in rabbitMQ, when not already declared. - * - * @param string $name - * @param string $type - * @param bool $durable - * @param bool $autoDelete - * @param array $arguments - * @return void */ public function declareExchange( string $name, @@ -412,9 +386,6 @@ public function declareExchange( /** * Delete a exchange from rabbitMQ, only when present in RabbitMQ. * - * @param string $name - * @param bool $unused - * @return void * * @throws AMQPProtocolChannelException */ @@ -437,8 +408,6 @@ public function deleteExchange(string $name, bool $unused = false): void * Checks if the given queue already present/defined in RabbitMQ. * Returns false when when the queue is missing. * - * @param string|null $name - * @return bool * * @throws AMQPProtocolChannelException */ @@ -462,12 +431,6 @@ public function isQueueExists(string $name = null): bool /** * Declare a queue in rabbitMQ, when not already declared. - * - * @param string $name - * @param bool $durable - * @param bool $autoDelete - * @param array $arguments - * @return void */ public function declareQueue( string $name, @@ -493,10 +456,6 @@ public function declareQueue( /** * Delete a queue from rabbitMQ, only when present in RabbitMQ. * - * @param string $name - * @param bool $if_unused - * @param bool $if_empty - * @return void * * @throws AMQPProtocolChannelException */ @@ -511,11 +470,6 @@ public function deleteQueue(string $name, bool $if_unused = false, bool $if_empt /** * Bind a queue to an exchange. - * - * @param string $queue - * @param string $exchange - * @param string $routingKey - * @return void */ public function bindQueue(string $queue, string $exchange, string $routingKey = ''): void { @@ -532,9 +486,6 @@ public function bindQueue(string $queue, string $exchange, string $routingKey = /** * Purge the queue of messages. - * - * @param string|null $queue - * @return void */ public function purge(string $queue = null): void { @@ -546,9 +497,6 @@ public function purge(string $queue = null): void /** * Acknowledge the message. - * - * @param RabbitMQJob $job - * @return void */ public function ack(RabbitMQJob $job): void { @@ -557,10 +505,6 @@ public function ack(RabbitMQJob $job): void /** * Reject the message. - * - * @param RabbitMQJob $job - * @param bool $requeue - * @return void */ public function reject(RabbitMQJob $job, bool $requeue = false): void { @@ -570,9 +514,6 @@ public function reject(RabbitMQJob $job, bool $requeue = false): void /** * Create a AMQP message. * - * @param $payload - * @param int $attempts - * @return array * * @throws JsonException */ @@ -619,7 +560,6 @@ protected function createMessage($payload, int $attempts = 0): array * @param string|object $job * @param string $queue * @param mixed $data - * @return array */ protected function createPayloadArray($job, $queue, $data = ''): array { @@ -630,8 +570,6 @@ protected function createPayloadArray($job, $queue, $data = ''): array /** * Get a random ID string. - * - * @return string */ protected function getRandomId(): string { @@ -641,7 +579,6 @@ protected function getRandomId(): string /** * Close the connection to RabbitMQ. * - * @return void * * @throws Exception */ @@ -660,9 +597,6 @@ public function close(): void /** * Get the Queue arguments. - * - * @param string $destination - * @return array */ protected function getQueueArguments(string $destination): array { @@ -690,10 +624,6 @@ protected function getQueueArguments(string $destination): array /** * Get the Delay queue arguments. - * - * @param string $destination - * @param int $ttl - * @return array */ protected function getDelayQueueArguments(string $destination, int $ttl): array { @@ -707,8 +637,6 @@ protected function getDelayQueueArguments(string $destination, int $ttl): array /** * Returns &true;, if delayed messages should be prioritized. - * - * @return bool */ protected function isPrioritizeDelayed(): bool { @@ -721,8 +649,6 @@ protected function isPrioritizeDelayed(): bool * Using more priority layers, will consume more CPU resources and would affect runtimes. * * @see https://www.rabbitmq.com/priority.html - * - * @return int */ protected function getQueueMaxPriority(): int { @@ -731,9 +657,6 @@ protected function getQueueMaxPriority(): int /** * Get the exchange name, or &null; as default value. - * - * @param string|null $exchange - * @return string|null */ protected function getExchange(string $exchange = null): ?string { @@ -743,9 +666,6 @@ protected function getExchange(string $exchange = null): ?string /** * Get the routing-key for when you use exchanges * The default routing-key is the given destination. - * - * @param string $destination - * @return string */ protected function getRoutingKey(string $destination): string { @@ -754,9 +674,6 @@ protected function getRoutingKey(string $destination): string /** * Get the exchangeType, or AMQPExchangeType::DIRECT as default. - * - * @param string|null $type - * @return string */ protected function getExchangeType(?string $type = null): string { @@ -768,8 +685,6 @@ protected function getExchangeType(?string $type = null): string /** * Returns &true;, if failed messages should be rerouted. - * - * @return bool */ protected function isRerouteFailed(): bool { @@ -778,8 +693,6 @@ protected function isRerouteFailed(): bool /** * Returns &true;, if declared queue must be quorum queue. - * - * @return bool */ protected function isQuorum(): bool { @@ -788,9 +701,6 @@ protected function isQuorum(): bool /** * Get the exchange for failed messages. - * - * @param string|null $exchange - * @return string|null */ protected function getFailedExchange(string $exchange = null): ?string { @@ -800,9 +710,6 @@ protected function getFailedExchange(string $exchange = null): ?string /** * Get the routing-key for failed messages * The default routing-key is the given destination substituted by '.failed'. - * - * @param string $destination - * @return string */ protected function getFailedRoutingKey(string $destination): string { @@ -811,9 +718,6 @@ protected function getFailedRoutingKey(string $destination): string /** * Checks if the exchange was already declared. - * - * @param string $name - * @return bool */ protected function isExchangeDeclared(string $name): bool { @@ -822,9 +726,6 @@ protected function isExchangeDeclared(string $name): bool /** * Checks if the queue was already declared. - * - * @param string $name - * @return bool */ protected function isQueueDeclared(string $name): bool { @@ -834,10 +735,7 @@ protected function isQueueDeclared(string $name): bool /** * Declare the destination when necessary. * - * @param string $destination - * @param string|null $exchange * @param string|null $exchangeType - * @return void * * @throws AMQPProtocolChannelException */ @@ -867,10 +765,6 @@ protected function declareDestination( /** * Determine all publish properties. - * - * @param $queue - * @param array $options - * @return array */ protected function publishProperties($queue, array $options = []): array { diff --git a/tests/Functional/TestCase.php b/tests/Functional/TestCase.php index 40e3e116..01b3c069 100644 --- a/tests/Functional/TestCase.php +++ b/tests/Functional/TestCase.php @@ -161,9 +161,6 @@ protected function getEnvironmentSetUp($app): void } /** - * @param $object - * @param string $method - * @param array $parameters * @return mixed * * @throws Exception From 2d4eb2f92a0d5d49d810128f782df17d5fe328a8 Mon Sep 17 00:00:00 2001 From: Seb Date: Tue, 21 Feb 2023 13:37:57 -0800 Subject: [PATCH 248/375] try enabling laravel 10 --- .github/workflows/tests.yml | 6 +++++- composer.json | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 5633c682..1c4400bf 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -15,6 +15,10 @@ jobs: matrix: php: ['8.0', '8.1', '8.2'] stability: [prefer-lowest, prefer-stable] + laravel: ['^9.0', '^10.0'] + exclude: + - php: '8.0' + laravel: '^10.0' name: PHP ${{ matrix.php }} - ${{ matrix.stability }} @@ -40,7 +44,7 @@ jobs: run: docker-compose up -d rabbitmq - name: Install dependencies - run: composer update --${{ matrix.stability }} --prefer-dist --no-interaction --no-progress + run: composer update --with='illuminate/queue:${{matrix.laravel}}' --${{ matrix.stability }} --prefer-dist --no-interaction --no-progress - name: Run Laravel Pint run: ./vendor/bin/pint --test diff --git a/composer.json b/composer.json index 15bf6e85..9c3c12cf 100644 --- a/composer.json +++ b/composer.json @@ -11,7 +11,7 @@ "require": { "php": "^8.0", "ext-json": "*", - "illuminate/queue": "^9.0", + "illuminate/queue": "^9.0|^10.0", "php-amqplib/php-amqplib": "^3.0" }, "require-dev": { From 239110e41e078aa304efa1823b2af80717e225cd Mon Sep 17 00:00:00 2001 From: Seb Date: Tue, 21 Feb 2023 13:39:23 -0800 Subject: [PATCH 249/375] test names --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 1c4400bf..f78ab11d 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -20,7 +20,7 @@ jobs: - php: '8.0' laravel: '^10.0' - name: PHP ${{ matrix.php }} - ${{ matrix.stability }} + name: 'PHP ${{ matrix.php }} - ${{ matrix.stability }} - Laravel: ${{matrix.laravel}}' steps: - name: Checkout code From 1a375f3e70249f0c33e97eca064e9eec47acbf2a Mon Sep 17 00:00:00 2001 From: Seb Date: Tue, 21 Feb 2023 14:04:51 -0800 Subject: [PATCH 250/375] enable more lara10 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 9c3c12cf..9833f04e 100644 --- a/composer.json +++ b/composer.json @@ -18,7 +18,7 @@ "phpunit/phpunit": "^9.3", "mockery/mockery": "^1.0", "laravel/horizon": "^5.0", - "orchestra/testbench": "^7.0", + "orchestra/testbench": "^7.0|^8.0", "laravel/pint": "^1.2" }, "autoload": { From 07a834fb80e4feb35c6e81554a8e0a1e38b85686 Mon Sep 17 00:00:00 2001 From: Seb Date: Tue, 21 Feb 2023 14:06:49 -0800 Subject: [PATCH 251/375] stability last in name --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index f78ab11d..5178bcf2 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -20,7 +20,7 @@ jobs: - php: '8.0' laravel: '^10.0' - name: 'PHP ${{ matrix.php }} - ${{ matrix.stability }} - Laravel: ${{matrix.laravel}}' + name: 'PHP ${{ matrix.php }} - Laravel: ${{matrix.laravel}} - ${{ matrix.stability }}' steps: - name: Checkout code From deac081c9005a59d15b69a4cb4b2591258270e20 Mon Sep 17 00:00:00 2001 From: Seb Date: Tue, 21 Feb 2023 15:28:49 -0800 Subject: [PATCH 252/375] tentative constraint --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 5178bcf2..b2660bae 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -15,7 +15,7 @@ jobs: matrix: php: ['8.0', '8.1', '8.2'] stability: [prefer-lowest, prefer-stable] - laravel: ['^9.0', '^10.0'] + laravel: ['^9.0 < 10.0', '^10.0'] exclude: - php: '8.0' laravel: '^10.0' From 9109ba144e48598da2d5a24bb7c439a8c68e761f Mon Sep 17 00:00:00 2001 From: Seb Date: Tue, 21 Feb 2023 15:40:05 -0800 Subject: [PATCH 253/375] stable or nothing --- .github/workflows/tests.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index b2660bae..f65bf2e0 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -14,8 +14,8 @@ jobs: fail-fast: true matrix: php: ['8.0', '8.1', '8.2'] - stability: [prefer-lowest, prefer-stable] - laravel: ['^9.0 < 10.0', '^10.0'] + stability: ['', '--prefer-stable'] + laravel: ['^9.0', '^10.0'] exclude: - php: '8.0' laravel: '^10.0' @@ -44,7 +44,7 @@ jobs: run: docker-compose up -d rabbitmq - name: Install dependencies - run: composer update --with='illuminate/queue:${{matrix.laravel}}' --${{ matrix.stability }} --prefer-dist --no-interaction --no-progress + run: composer update --with='illuminate/queue:${{matrix.laravel}}' ${{ matrix.stability }} --prefer-dist --no-interaction --no-progress - name: Run Laravel Pint run: ./vendor/bin/pint --test From 67f3b9f0546e8194e3572eab76e7cbf40e1c1e06 Mon Sep 17 00:00:00 2001 From: Seb Date: Tue, 21 Feb 2023 15:45:01 -0800 Subject: [PATCH 254/375] stable or nothing --- .github/workflows/tests.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index f65bf2e0..3ccf92f5 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -14,8 +14,8 @@ jobs: fail-fast: true matrix: php: ['8.0', '8.1', '8.2'] - stability: ['', '--prefer-stable'] - laravel: ['^9.0', '^10.0'] + stability: ['prefer-lowest', prefer-stable] + laravel: ['>=9.0 <10', '^10.0'] exclude: - php: '8.0' laravel: '^10.0' @@ -44,7 +44,7 @@ jobs: run: docker-compose up -d rabbitmq - name: Install dependencies - run: composer update --with='illuminate/queue:${{matrix.laravel}}' ${{ matrix.stability }} --prefer-dist --no-interaction --no-progress + run: composer update --with='illuminate/queue:${{matrix.laravel}}' --${{ matrix.stability }} --prefer-dist --no-interaction --no-progress - name: Run Laravel Pint run: ./vendor/bin/pint --test From 77004c5d0d49e48b129975a0721503561d749392 Mon Sep 17 00:00:00 2001 From: Seb Date: Tue, 21 Feb 2023 15:55:20 -0800 Subject: [PATCH 255/375] was that it --- .github/workflows/tests.yml | 2 +- composer.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 3ccf92f5..828d3eef 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -15,7 +15,7 @@ jobs: matrix: php: ['8.0', '8.1', '8.2'] stability: ['prefer-lowest', prefer-stable] - laravel: ['>=9.0 <10', '^10.0'] + laravel: ['^9.0', '^10.0'] exclude: - php: '8.0' laravel: '^10.0' diff --git a/composer.json b/composer.json index 9833f04e..4dbf9b34 100644 --- a/composer.json +++ b/composer.json @@ -11,7 +11,7 @@ "require": { "php": "^8.0", "ext-json": "*", - "illuminate/queue": "^9.0|^10.0", + "illuminate/queue": "^9.0||^10.0", "php-amqplib/php-amqplib": "^3.0" }, "require-dev": { From babf55f6d64228237bbf6fb06885c247a37b9b30 Mon Sep 17 00:00:00 2001 From: Seb Date: Tue, 21 Feb 2023 15:59:55 -0800 Subject: [PATCH 256/375] use as direct dependency to force it --- .github/workflows/tests.yml | 2 +- composer.json | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 828d3eef..37c80375 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -44,7 +44,7 @@ jobs: run: docker-compose up -d rabbitmq - name: Install dependencies - run: composer update --with='illuminate/queue:${{matrix.laravel}}' --${{ matrix.stability }} --prefer-dist --no-interaction --no-progress + run: composer update --with='laravel/framework:${{matrix.laravel}}' --${{ matrix.stability }} --prefer-dist --no-interaction --no-progress - name: Run Laravel Pint run: ./vendor/bin/pint --test diff --git a/composer.json b/composer.json index 4dbf9b34..5461314d 100644 --- a/composer.json +++ b/composer.json @@ -11,7 +11,7 @@ "require": { "php": "^8.0", "ext-json": "*", - "illuminate/queue": "^9.0||^10.0", + "illuminate/queue": "^9.0|^10.0", "php-amqplib/php-amqplib": "^3.0" }, "require-dev": { @@ -19,7 +19,8 @@ "mockery/mockery": "^1.0", "laravel/horizon": "^5.0", "orchestra/testbench": "^7.0|^8.0", - "laravel/pint": "^1.2" + "laravel/pint": "^1.2", + "laravel/framework": "^9.0|^10.0" }, "autoload": { "psr-4": { From 0cccc92794b4d85268ff9e6a74293deb97de8245 Mon Sep 17 00:00:00 2001 From: Seb Date: Wed, 22 Feb 2023 10:42:55 -0800 Subject: [PATCH 257/375] update readme --- CHANGELOG-13x.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG-13x.md b/CHANGELOG-13x.md index 0f9e7678..0631fc9d 100644 --- a/CHANGELOG-13x.md +++ b/CHANGELOG-13x.md @@ -2,7 +2,11 @@ All notable changes to this project will be documented in this file. -## [Unreleased](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v13.1.0...master) +## [unreleased](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v13.2.0...master) + +## [13.2.0](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v13.1.0...13.2.0) + +- Compatibility with Laravel 10 [#525](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/525) ## [13.1.0 (2023-01-25)](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v13.0.1...v13.1.0) From f2f7a5e7a907ad2bb39fe0113add5220ead82338 Mon Sep 17 00:00:00 2001 From: Emiel Bom Date: Thu, 2 Mar 2023 19:09:23 +0100 Subject: [PATCH 258/375] added: - factory for making a queue. - default config object - rabbitmq queue contract fixed: - code quality issues - tests (for checking moved config items) removed: - unnecessary config - unnecessary methods for returning just the config --- config/rabbitmq.php | 4 +- src/Contracts/RabbitMQQueueContract.php | 14 ++ src/Horizon/RabbitMQQueue.php | 25 +-- src/Queue/Connectors/RabbitMQConnector.php | 45 +---- src/Queue/QueueConfig.php | 213 +++++++++++++++++++++ src/Queue/QueueFactory.php | 65 +++++++ src/Queue/RabbitMQQueue.php | 210 +++++++------------- tests/Functional/RabbitMQQueueTest.php | 81 ++++---- 8 files changed, 415 insertions(+), 242 deletions(-) create mode 100644 src/Contracts/RabbitMQQueueContract.php create mode 100644 src/Queue/QueueConfig.php create mode 100644 src/Queue/QueueFactory.php diff --git a/config/rabbitmq.php b/config/rabbitmq.php index d5390a5c..900e3b74 100644 --- a/config/rabbitmq.php +++ b/config/rabbitmq.php @@ -29,9 +29,7 @@ 'verify_peer' => env('RABBITMQ_SSL_VERIFY_PEER', true), 'passphrase' => env('RABBITMQ_SSL_PASSPHRASE', null), ], - 'queue' => [ - 'job' => VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Jobs\RabbitMQJob::class, - ], + 'queue' => [], ], /* diff --git a/src/Contracts/RabbitMQQueueContract.php b/src/Contracts/RabbitMQQueueContract.php new file mode 100644 index 00000000..cf04a66e --- /dev/null +++ b/src/Contracts/RabbitMQQueueContract.php @@ -0,0 +1,14 @@ +size($queue); } @@ -46,8 +43,10 @@ public function push($job, $data = '', $queue = null) /** * {@inheritdoc} + * + * @throws BindingResolutionException */ - public function pushRaw($payload, $queue = null, array $options = []) + public function pushRaw($payload, $queue = null, array $options = []): int|string|null { $payload = (new JobPayload($payload))->prepare($this->lastPushed)->value; @@ -58,8 +57,10 @@ public function pushRaw($payload, $queue = null, array $options = []) /** * {@inheritdoc} + * + * @throws BindingResolutionException */ - public function later($delay, $job, $data = '', $queue = null) + public function later($delay, $job, $data = '', $queue = null): mixed { $payload = (new JobPayload($this->createPayload($job, $data)))->prepare($job)->value; @@ -80,16 +81,6 @@ public function pop($queue = null) }); } - /** - * {@inheritdoc} - */ - public function release($delay, $job, $data, $queue, $attempts = 0) - { - $this->lastPushed = $job; - - return parent::release($delay, $job, $data, $queue, $attempts); - } - /** * Fire the job deleted event. * diff --git a/src/Queue/Connectors/RabbitMQConnector.php b/src/Queue/Connectors/RabbitMQConnector.php index 9fc8615f..8d8498b4 100644 --- a/src/Queue/Connectors/RabbitMQConnector.php +++ b/src/Queue/Connectors/RabbitMQConnector.php @@ -9,23 +9,23 @@ use Illuminate\Queue\Events\JobFailed; use Illuminate\Queue\Events\WorkerStopping; use Illuminate\Support\Arr; -use InvalidArgumentException; use PhpAmqpLib\Connection\AbstractConnection; use PhpAmqpLib\Connection\AMQPLazyConnection; use VladimirYuldashev\LaravelQueueRabbitMQ\Horizon\Listeners\RabbitMQFailedEvent; use VladimirYuldashev\LaravelQueueRabbitMQ\Horizon\RabbitMQQueue as HorizonRabbitMQQueue; +use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\QueueFactory; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue; class RabbitMQConnector implements ConnectorInterface { - /** - * @var Dispatcher - */ - private $dispatcher; + private Dispatcher $dispatcher; + + private QueueFactory $queue; public function __construct(Dispatcher $dispatcher) { $this->dispatcher = $dispatcher; + $this->queue = new QueueFactory(); } /** @@ -37,19 +37,10 @@ public function __construct(Dispatcher $dispatcher) */ public function connect(array $config): Queue { + // Todo Create ConnectionFactory removing all deprecated dependicies $connection = $this->createConnection(Arr::except($config, 'options.queue')); - $queue = $this->createQueue( - Arr::get($config, 'worker', 'default'), - $connection, - $config['queue'], - Arr::get($config, 'after_commit', false), - Arr::get($config, 'options.queue', []) - ); - - if (! $queue instanceof RabbitMQQueue) { - throw new InvalidArgumentException('Invalid worker.'); - } + $queue = $this->queue->make($config)->setConnection($connection); if ($queue instanceof HorizonRabbitMQQueue) { $this->dispatcher->listen(JobFailed::class, RabbitMQFailedEvent::class); @@ -79,28 +70,6 @@ protected function createConnection(array $config): AbstractConnection ); } - /** - * Create a queue for the worker. - * - * @return HorizonRabbitMQQueue|RabbitMQQueue|Queue - */ - protected function createQueue( - string $worker, - AbstractConnection $connection, - string $queue, - bool $dispatchAfterCommit, - array $options = [] - ) { - switch ($worker) { - case 'default': - return new RabbitMQQueue($connection, $queue, $dispatchAfterCommit, $options); - case 'horizon': - return new HorizonRabbitMQQueue($connection, $queue, $dispatchAfterCommit, $options); - default: - return new $worker($connection, $queue, $options); - } - } - /** * Recursively filter only null values. */ diff --git a/src/Queue/QueueConfig.php b/src/Queue/QueueConfig.php new file mode 100644 index 00000000..6c4b5d49 --- /dev/null +++ b/src/Queue/QueueConfig.php @@ -0,0 +1,213 @@ +queue; + } + + public function setQueue(?string $queue): QueueConfig + { + $this->queue = $queue ?: 'default'; + + return $this; + } + + public function isDispatchAfterCommit(): bool + { + return $this->dispatchAfterCommit; + } + + public function setDispatchAfterCommit($dispatchAfterCommit): QueueConfig + { + $this->dispatchAfterCommit = ! empty($dispatchAfterCommit); + + return $this; + } + + public function getOptions(): array + { + return $this->options; + } + + public function setOptions(?array $options): QueueConfig + { + $this->options = $options ?: []; + + return $this; + } + + public function getAbstractJob(): string + { + return $this->abstractJob; + } + + public function setAbstractJob(?string $abstract): QueueConfig + { + $this->abstractJob = $abstract ?: RabbitMQJob::class; + + return $this; + } + + /** + * Returns &true;, if delayed messages should be prioritized. + */ + public function isPrioritizeDelayed(): bool + { + return $this->prioritizeDelayed; + } + + public function setPrioritizeDelayed($prioritizeDelayed): QueueConfig + { + $this->prioritizeDelayed = ! empty($prioritizeDelayed); + + return $this; + } + + /** + * Returns a integer with a default of '2' for when using prioritization on delayed messages. + * If priority queues are desired, we recommend using between 1 and 10. + * Using more priority layers, will consume more CPU resources and would affect runtimes. + * + * @see https://www.rabbitmq.com/priority.html + */ + public function getQueueMaxPriority(): int + { + return $this->queueMaxPriority; + } + + public function setQueueMaxPriority($queueMaxPriority): QueueConfig + { + $this->queueMaxPriority = is_numeric($queueMaxPriority) ? (int) $queueMaxPriority : 2; + + return $this; + } + + /** + * Get the exchange name, or &null; as default value. + */ + public function getExchange(): ?string + { + return $this->exchange; + } + + public function setExchange(?string $exchange): QueueConfig + { + $this->exchange = $exchange ?: null; + + return $this; + } + + public function getExchangeType(): string + { + return $this->exchangeType; + } + + public function setExchangeType(?string $exchangeType): QueueConfig + { + $this->exchangeType = $exchangeType ?: 'direct'; + + return $this; + } + + /** + * @return string + */ + public function getExchangeRoutingKey(): ?string + { + return $this->exchangeRoutingKey; + } + + public function setExchangeRoutingKey(?string $exchangeRoutingKey): QueueConfig + { + $this->exchangeRoutingKey = $exchangeRoutingKey ?: '%s'; + + return $this; + } + + /** + * Returns &true;, if failed messages should be rerouted. + */ + public function isRerouteFailed(): bool + { + return $this->rerouteFailed; + } + + public function setRerouteFailed($rerouteFailed): QueueConfig + { + $this->rerouteFailed = ! empty($rerouteFailed); + + return $this; + } + + public function getFailedExchange(): ?string + { + return $this->failedExchange; + } + + public function setFailedExchange(?string $failedExchange): QueueConfig + { + $this->failedExchange = $failedExchange ?: null; + + return $this; + } + + /** + * Get the routing-key for failed messages + * The default routing-key is the given destination substituted by '.failed'. + */ + public function getFailedRoutingKey(): string + { + return $this->failedRoutingKey; + } + + public function setFailedRoutingKey(?string $failedRoutingKey): QueueConfig + { + $this->failedRoutingKey = $failedRoutingKey ?: '%s.failed'; + + return $this; + } + + public function isQuorum(): bool + { + return $this->quorum; + } + + public function setQuorum($quorum): QueueConfig + { + $this->quorum = ! empty($quorum); + + return $this; + } +} diff --git a/src/Queue/QueueFactory.php b/src/Queue/QueueFactory.php new file mode 100644 index 00000000..ccbfd51d --- /dev/null +++ b/src/Queue/QueueFactory.php @@ -0,0 +1,65 @@ +createQueueConfig($config); + $worker = Arr::get($config, 'worker', 'default'); + + if (strtolower($worker) == 'default') { + return $this->queue = new RabbitMQQueue($queueConfig); + } + + if (strtolower($worker) == 'horizon') { + return $this->queue = new HorizonRabbitMQQueue($queueConfig); + } + + return $this->queue = new $worker($queueConfig); + } + + /** + * Create a config object from config array + */ + private function createQueueConfig(array $config = []): QueueConfig + { + return tap(new QueueConfig(), function (QueueConfig $queueConfig) use ($config) { + if (! empty($queue = Arr::get($config, 'queue'))) { + $queueConfig->setQueue($queue); + } + if (! empty($afterCommit = Arr::get($config, 'after_commit'))) { + $queueConfig->setDispatchAfterCommit($afterCommit); + } + + if (! empty($queueOptionsConfig = Arr::get($config, 'options.queue'))) { + $queueConfig + ->setAbstractJob(Arr::pull($queueOptionsConfig, 'job')) + // Feature: Prioritize delayed messages. + ->setPrioritizeDelayed(Arr::pull($queueOptionsConfig, 'prioritize_delayed')) + ->setQueueMaxPriority(Arr::pull($queueOptionsConfig, 'queue_max_priority')) + // Feature: Working with Exchange and routing-keys + ->setExchange(Arr::pull($queueOptionsConfig, 'exchange')) + ->setExchangeType(Arr::pull($queueOptionsConfig, 'exchange_type')) + ->setExchangeRoutingKey(Arr::pull($queueOptionsConfig, 'exchange_routing_key')) + // Feature: Reroute failed messages + ->setRerouteFailed(Arr::pull($queueOptionsConfig, 'reroute_failed')) + ->setFailedExchange(Arr::pull($queueOptionsConfig, 'failed_exchange')) + ->setFailedRoutingKey(Arr::pull($queueOptionsConfig, 'failed_routing_key')) + // Feature: Mark queue as quorum + ->setQuorum(Arr::pull($queueOptionsConfig, 'quorum')) + // All extra options not defined + ->setOptions($queueOptionsConfig); + } + }); + } +} diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index 030a4734..59d98f3a 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -10,7 +10,6 @@ use Illuminate\Queue\Queue; use Illuminate\Support\Arr; use Illuminate\Support\Str; -use JsonException; use PhpAmqpLib\Channel\AMQPChannel; use PhpAmqpLib\Connection\AbstractConnection; use PhpAmqpLib\Exception\AMQPChannelClosedException; @@ -21,78 +20,53 @@ use PhpAmqpLib\Message\AMQPMessage; use PhpAmqpLib\Wire\AMQPTable; use Throwable; +use VladimirYuldashev\LaravelQueueRabbitMQ\Contracts\RabbitMQQueueContract; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Jobs\RabbitMQJob; -class RabbitMQQueue extends Queue implements QueueContract +class RabbitMQQueue extends Queue implements QueueContract, RabbitMQQueueContract { /** * The RabbitMQ connection instance. - * - * @var AbstractConnection */ - protected $connection; + protected AbstractConnection $connection; /** * The RabbitMQ channel instance. - * - * @var AMQPChannel */ - protected $channel; - - /** - * The name of the default queue. - * - * @var string - */ - protected $default; + protected AMQPChannel $channel; /** * List of already declared exchanges. - * - * @var array */ - protected $exchanges = []; + protected array $exchanges = []; /** * List of already declared queues. - * - * @var array */ - protected $queues = []; + protected array $queues = []; /** * List of already bound queues to exchanges. - * - * @var array */ - protected $boundQueues = []; + protected array $boundQueues = []; /** * Current job being processed. - * - * @var RabbitMQJob */ - protected $currentJob; + protected RabbitMQJob $currentJob; /** - * @var array + * Holds the Configuration */ - protected $options; + protected QueueConfig $config; /** * RabbitMQQueue constructor. */ - public function __construct( - AbstractConnection $connection, - string $default, - bool $dispatchAfterCommit = false, - array $options = [] - ) { - $this->connection = $connection; - $this->channel = $connection->channel(); - $this->default = $default; - $this->options = $options; - $this->dispatchAfterCommit = $dispatchAfterCommit; + public function __construct(QueueConfig $config) + { + $this->config = $config; + $this->dispatchAfterCommit = $config->isDispatchAfterCommit(); } /** @@ -109,7 +83,7 @@ public function size($queue = null): int } // create a temporary channel, so the main channel will not be closed on exception - $channel = $this->connection->channel(); + $channel = $this->getConnection()->channel(); [, $size] = $channel->queue_declare($queue, true); $channel->close(); @@ -139,7 +113,7 @@ function ($payload, $queue) { * * @throws AMQPProtocolChannelException */ - public function pushRaw($payload, $queue = null, array $options = []) + public function pushRaw($payload, $queue = null, array $options = []): int|string|null { [$destination, $exchange, $exchangeType, $attempts] = $this->publishProperties($queue, $options); @@ -147,7 +121,7 @@ public function pushRaw($payload, $queue = null, array $options = []) [$message, $correlationId] = $this->createMessage($payload, $attempts); - $this->channel->basic_publish($message, $exchange, $destination, true, false); + $this->getChannel()->basic_publish($message, $exchange, $destination, true); return $correlationId; } @@ -157,7 +131,7 @@ public function pushRaw($payload, $queue = null, array $options = []) * * @throws AMQPProtocolChannelException */ - public function later($delay, $job, $data = '', $queue = null) + public function later($delay, $job, $data = '', $queue = null): mixed { return $this->enqueueUsing( $job, @@ -172,12 +146,10 @@ function ($payload, $queue, $delay) { /** * @param null $queue - * @param int $attempts - * @return mixed * * @throws AMQPProtocolChannelException */ - public function laterRaw($delay, $payload, $queue = null, $attempts = 0) + public function laterRaw($delay, string $payload, $queue = null, int $attempts = 0): int|string|null { $ttl = $this->secondsUntil($delay) * 1000; @@ -199,8 +171,8 @@ public function laterRaw($delay, $payload, $queue = null, $attempts = 0) [$message, $correlationId] = $this->createMessage($payload, $attempts); - // Publish directly on the delayQueue, no need to publish trough an exchange. - $this->channel->basic_publish($message, null, $destination, true, false); + // Publish directly on the delayQueue, no need to publish through an exchange. + $this->getChannel()->basic_publish($message, null, $destination, true); return $correlationId; } @@ -216,16 +188,13 @@ public function bulk($jobs, $data = '', $queue = null): void $this->bulkRaw($this->createPayload($job, $queue, $data), $queue, ['job' => $job]); } - $this->channel->publish_batch(); + $this->getChannel()->publish_batch(); } /** - * @param null $queue - * @return mixed - * * @throws AMQPProtocolChannelException */ - public function bulkRaw(string $payload, $queue = null, array $options = []) + public function bulkRaw(string $payload, string $queue = null, array $options = []): int|string|null { [$destination, $exchange, $exchangeType, $attempts] = $this->publishProperties($queue, $options); @@ -233,7 +202,7 @@ public function bulkRaw(string $payload, $queue = null, array $options = []) [$message, $correlationId] = $this->createMessage($payload, $attempts); - $this->channel->batch_basic_publish($message, $exchange, $destination); + $this->getChannel()->batch_basic_publish($message, $exchange, $destination); return $correlationId; } @@ -251,7 +220,7 @@ public function pop($queue = null) $job = $this->getJobClass(); /** @var AMQPMessage|null $message */ - if ($message = $this->channel->basic_get($queue)) { + if ($message = $this->getChannel()->basic_get($queue)) { return $this->currentJob = new $job( $this->container, $this, @@ -261,12 +230,12 @@ public function pop($queue = null) ); } } catch (AMQPProtocolChannelException $exception) { - // If there is not exchange or queue AMQP will throw exception with code 404 + // If there is no exchange or queue AMQP will throw exception with code 404 // We need to catch it and return null if ($exception->amqp_reply_code === 404) { // Because of the channel exception the channel was closed and removed. // We have to open a new channel. Because else the worker(s) are stuck in a loop, without processing. - $this->channel = $this->connection->channel(); + $this->channel = $this->getConnection()->channel(); return null; } @@ -292,6 +261,14 @@ public function getConnection(): AbstractConnection return $this->connection; } + public function setConnection(AbstractConnection $connection): RabbitMQQueue + { + $this->connection = $connection; + $this->channel = $connection->channel(); + + return $this; + } + public function getChannel(): AMQPChannel { return $this->channel; @@ -305,7 +282,7 @@ public function getChannel(): AMQPChannel */ public function getJobClass(): string { - $job = Arr::get($this->options, 'job', RabbitMQJob::class); + $job = $this->getConfig()->getAbstractJob(); throw_if( ! is_a($job, RabbitMQJob::class, true), @@ -323,12 +300,12 @@ public function getJobClass(): string */ public function getQueue($queue = null): string { - return $queue ?: $this->default; + return $queue ?: $this->getConfig()->getQueue(); } /** * Checks if the given exchange already present/defined in RabbitMQ. - * Returns false when when the exchange is missing. + * Returns false when the exchange is missing. * * * @throws AMQPProtocolChannelException @@ -341,7 +318,7 @@ public function isExchangeExists(string $exchange): bool try { // create a temporary channel, so the main channel will not be closed on exception - $channel = $this->connection->channel(); + $channel = $this->getConnection()->channel(); $channel->exchange_declare($exchange, '', true); $channel->close(); @@ -358,7 +335,7 @@ public function isExchangeExists(string $exchange): bool } /** - * Declare a exchange in rabbitMQ, when not already declared. + * Declare an exchange in rabbitMQ, when not already declared. */ public function declareExchange( string $name, @@ -371,7 +348,7 @@ public function declareExchange( return; } - $this->channel->exchange_declare( + $this->getChannel()->exchange_declare( $name, $type, false, @@ -384,7 +361,7 @@ public function declareExchange( } /** - * Delete a exchange from rabbitMQ, only when present in RabbitMQ. + * Delete an exchange from rabbitMQ, only when present in RabbitMQ. * * * @throws AMQPProtocolChannelException @@ -398,7 +375,7 @@ public function deleteExchange(string $name, bool $unused = false): void $idx = array_search($name, $this->exchanges); unset($this->exchanges[$idx]); - $this->channel->exchange_delete( + $this->getChannel()->exchange_delete( $name, $unused ); @@ -406,7 +383,7 @@ public function deleteExchange(string $name, bool $unused = false): void /** * Checks if the given queue already present/defined in RabbitMQ. - * Returns false when when the queue is missing. + * Returns false when the queue is missing. * * * @throws AMQPProtocolChannelException @@ -415,7 +392,7 @@ public function isQueueExists(string $name = null): bool { try { // create a temporary channel, so the main channel will not be closed on exception - $channel = $this->connection->channel(); + $channel = $this->getConnection()->channel(); $channel->queue_declare($this->getQueue($name), true); $channel->close(); @@ -442,7 +419,7 @@ public function declareQueue( return; } - $this->channel->queue_declare( + $this->getChannel()->queue_declare( $name, false, $durable, @@ -465,7 +442,7 @@ public function deleteQueue(string $name, bool $if_unused = false, bool $if_empt return; } - $this->channel->queue_delete($name, $if_unused, $if_empty); + $this->getChannel()->queue_delete($name, $if_unused, $if_empty); } /** @@ -481,7 +458,7 @@ public function bindQueue(string $queue, string $exchange, string $routingKey = return; } - $this->channel->queue_bind($queue, $exchange, $routingKey); + $this->getChannel()->queue_bind($queue, $exchange, $routingKey); } /** @@ -490,7 +467,7 @@ public function bindQueue(string $queue, string $exchange, string $routingKey = public function purge(string $queue = null): void { // create a temporary channel, so the main channel will not be closed on exception - $channel = $this->connection->channel(); + $channel = $this->getConnection()->channel(); $channel->queue_purge($this->getQueue($queue)); $channel->close(); } @@ -500,7 +477,7 @@ public function purge(string $queue = null): void */ public function ack(RabbitMQJob $job): void { - $this->channel->basic_ack($job->getRabbitMQMessage()->getDeliveryTag()); + $this->getChannel()->basic_ack($job->getRabbitMQMessage()->getDeliveryTag()); } /** @@ -508,14 +485,11 @@ public function ack(RabbitMQJob $job): void */ public function reject(RabbitMQJob $job, bool $requeue = false): void { - $this->channel->basic_reject($job->getRabbitMQMessage()->getDeliveryTag(), $requeue); + $this->getChannel()->basic_reject($job->getRabbitMQMessage()->getDeliveryTag(), $requeue); } /** * Create a AMQP message. - * - * - * @throws JsonException */ protected function createMessage($payload, int $attempts = 0): array { @@ -524,12 +498,12 @@ protected function createMessage($payload, int $attempts = 0): array 'delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT, ]; - $currentPayload = json_decode($payload, true, 512); + $currentPayload = json_decode($payload, true); if ($correlationId = $currentPayload['id'] ?? null) { $properties['correlation_id'] = $correlationId; } - if ($this->isPrioritizeDelayed()) { + if ($this->getConfig()->isPrioritizeDelayed()) { $properties['priority'] = $attempts; } @@ -584,13 +558,13 @@ protected function getRandomId(): string */ public function close(): void { - if ($this->currentJob && ! $this->currentJob->isDeletedOrReleased()) { + if (! $this->currentJob->isDeletedOrReleased()) { $this->reject($this->currentJob, true); } try { $this->connection->close(); - } catch (ErrorException $exception) { + } catch (ErrorException) { // Ignore the exception } } @@ -606,16 +580,16 @@ protected function getQueueArguments(string $destination): array // Messages with a priority which is higher than the queue's maximum, are treated as if they were // published with the maximum priority. // Quorum queues does not support priority. - if ($this->isPrioritizeDelayed() && ! $this->isQuorum()) { - $arguments['x-max-priority'] = $this->getQueueMaxPriority(); + if ($this->getConfig()->isPrioritizeDelayed() && ! $this->getConfig()->isQuorum()) { + $arguments['x-max-priority'] = $this->getConfig()->getQueueMaxPriority(); } - if ($this->isRerouteFailed()) { + if ($this->getConfig()->isRerouteFailed()) { $arguments['x-dead-letter-exchange'] = $this->getFailedExchange() ?? ''; $arguments['x-dead-letter-routing-key'] = $this->getFailedRoutingKey($destination); } - if ($this->isQuorum()) { + if ($this->getConfig()->isQuorum()) { $arguments['x-queue-type'] = 'quorum'; } @@ -635,32 +609,12 @@ protected function getDelayQueueArguments(string $destination, int $ttl): array ]; } - /** - * Returns &true;, if delayed messages should be prioritized. - */ - protected function isPrioritizeDelayed(): bool - { - return (bool) (Arr::get($this->options, 'prioritize_delayed') ?: false); - } - - /** - * Returns a integer with a default of '2' for when using prioritization on delayed messages. - * If priority queues are desired, we recommend using between 1 and 10. - * Using more priority layers, will consume more CPU resources and would affect runtimes. - * - * @see https://www.rabbitmq.com/priority.html - */ - protected function getQueueMaxPriority(): int - { - return (int) (Arr::get($this->options, 'queue_max_priority') ?: 2); - } - /** * Get the exchange name, or &null; as default value. */ protected function getExchange(string $exchange = null): ?string { - return $exchange ?: Arr::get($this->options, 'exchange') ?: null; + return $exchange ?: $this->getConfig()->getExchange(); } /** @@ -669,7 +623,7 @@ protected function getExchange(string $exchange = null): ?string */ protected function getRoutingKey(string $destination): string { - return ltrim(sprintf(Arr::get($this->options, 'exchange_routing_key') ?: '%s', $destination), '.'); + return ltrim(sprintf($this->getConfig()->getExchangeRoutingKey(), $destination), '.'); } /** @@ -677,26 +631,7 @@ protected function getRoutingKey(string $destination): string */ protected function getExchangeType(?string $type = null): string { - return @constant(AMQPExchangeType::class.'::'.Str::upper($type ?: Arr::get( - $this->options, - 'exchange_type' - ) ?: 'direct')) ?: AMQPExchangeType::DIRECT; - } - - /** - * Returns &true;, if failed messages should be rerouted. - */ - protected function isRerouteFailed(): bool - { - return (bool) (Arr::get($this->options, 'reroute_failed') ?: false); - } - - /** - * Returns &true;, if declared queue must be quorum queue. - */ - protected function isQuorum(): bool - { - return (bool) (Arr::get($this->options, 'quorum') ?: false); + return @constant(AMQPExchangeType::class.'::'.Str::upper($type ?: $this->getConfig()->getExchangeType())) ?: AMQPExchangeType::DIRECT; } /** @@ -704,7 +639,7 @@ protected function isQuorum(): bool */ protected function getFailedExchange(string $exchange = null): ?string { - return $exchange ?: Arr::get($this->options, 'failed_exchange') ?: null; + return $exchange ?: $this->getConfig()->getFailedExchange(); } /** @@ -713,7 +648,7 @@ protected function getFailedExchange(string $exchange = null): ?string */ protected function getFailedRoutingKey(string $destination): string { - return ltrim(sprintf(Arr::get($this->options, 'failed_routing_key') ?: '%s.failed', $destination), '.'); + return ltrim(sprintf($this->getConfig()->getFailedRoutingKey(), $destination), '.'); } /** @@ -735,21 +670,17 @@ protected function isQueueDeclared(string $name): bool /** * Declare the destination when necessary. * - * @param string|null $exchangeType * * @throws AMQPProtocolChannelException */ - protected function declareDestination( - string $destination, - ?string $exchange = null, - string $exchangeType = AMQPExchangeType::DIRECT - ): void { - // When a exchange is provided and no exchange is present in RabbitMQ, create an exchange. + protected function declareDestination(string $destination, ?string $exchange = null, string $exchangeType = AMQPExchangeType::DIRECT): void + { + // When an exchange is provided and no exchange is present in RabbitMQ, create an exchange. if ($exchange && ! $this->isExchangeExists($exchange)) { $this->declareExchange($exchange, $exchangeType); } - // When a exchange is provided, just return. + // When an exchange is provided, just return. if ($exchange) { return; } @@ -777,4 +708,9 @@ protected function publishProperties($queue, array $options = []): array return [$destination, $exchange, $exchangeType, $attempts]; } + + public function getConfig(): QueueConfig + { + return $this->config; + } } diff --git a/tests/Functional/RabbitMQQueueTest.php b/tests/Functional/RabbitMQQueueTest.php index 1c03fe99..535e6f74 100644 --- a/tests/Functional/RabbitMQQueueTest.php +++ b/tests/Functional/RabbitMQQueueTest.php @@ -11,7 +11,6 @@ class RabbitMQQueueTest extends BaseTestCase { public function testConnection(): void { - /** @var $queue RabbitMQQueue */ $queue = $this->connection(); $this->assertInstanceOf(RabbitMQQueue::class, $queue); @@ -22,51 +21,47 @@ public function testConnection(): void $this->assertInstanceOf(RabbitMQQueue::class, $queue); } - public function testRerouteFailed(): void + public function testConfigRerouteFailed(): void { - /** @var $queue RabbitMQQueue */ - $queue = $this->connection(); - $this->assertFalse($this->callMethod($queue, 'isRerouteFailed')); + $config = $this->connection()->getConfig(); + $this->assertFalse($this->callMethod($config, 'isRerouteFailed')); - $queue = $this->connection('rabbitmq-with-options'); - $this->assertTrue($this->callMethod($queue, 'isRerouteFailed')); + $config = $this->connection('rabbitmq-with-options')->getConfig(); + $this->assertTrue($this->callMethod($config, 'isRerouteFailed')); - $queue = $this->connection('rabbitmq-with-options-empty'); - $this->assertFalse($this->callMethod($queue, 'isRerouteFailed')); + $config = $this->connection('rabbitmq-with-options-empty')->getConfig(); + $this->assertFalse($this->callMethod($config, 'isRerouteFailed')); } - public function testPrioritizeDelayed(): void + public function testConfigPrioritizeDelayed(): void { - /** @var $queue RabbitMQQueue */ - $queue = $this->connection(); - $this->assertFalse($this->callMethod($queue, 'isPrioritizeDelayed')); + $config = $this->connection()->getConfig(); + $this->assertFalse($this->callMethod($config, 'isPrioritizeDelayed')); - $queue = $this->connection('rabbitmq-with-options'); - $this->assertTrue($this->callMethod($queue, 'isPrioritizeDelayed')); + $config = $this->connection('rabbitmq-with-options')->getConfig(); + $this->assertTrue($this->callMethod($config, 'isPrioritizeDelayed')); - $queue = $this->connection('rabbitmq-with-options-empty'); - $this->assertFalse($this->callMethod($queue, 'isPrioritizeDelayed')); + $config = $this->connection('rabbitmq-with-options-empty')->getConfig(); + $this->assertFalse($this->callMethod($config, 'isPrioritizeDelayed')); } public function testQueueMaxPriority(): void { - /** @var $queue RabbitMQQueue */ - $queue = $this->connection(); - $this->assertIsInt($this->callMethod($queue, 'getQueueMaxPriority')); - $this->assertSame(2, $this->callMethod($queue, 'getQueueMaxPriority')); + $config = $this->connection()->getConfig(); + $this->assertIsInt($this->callMethod($config, 'getQueueMaxPriority')); + $this->assertSame(2, $this->callMethod($config, 'getQueueMaxPriority')); - $queue = $this->connection('rabbitmq-with-options'); - $this->assertIsInt($this->callMethod($queue, 'getQueueMaxPriority')); - $this->assertSame(20, $this->callMethod($queue, 'getQueueMaxPriority')); + $config = $this->connection('rabbitmq-with-options')->getConfig(); + $this->assertIsInt($this->callMethod($config, 'getQueueMaxPriority')); + $this->assertSame(20, $this->callMethod($config, 'getQueueMaxPriority')); - $queue = $this->connection('rabbitmq-with-options-empty'); - $this->assertIsInt($this->callMethod($queue, 'getQueueMaxPriority')); - $this->assertSame(2, $this->callMethod($queue, 'getQueueMaxPriority')); + $config = $this->connection('rabbitmq-with-options-empty')->getConfig(); + $this->assertIsInt($this->callMethod($config, 'getQueueMaxPriority')); + $this->assertSame(2, $this->callMethod($config, 'getQueueMaxPriority')); } - public function testExchangeType(): void + public function testConfigExchangeType(): void { - /** @var $queue RabbitMQQueue */ $queue = $this->connection(); $this->assertSame(AMQPExchangeType::DIRECT, $this->callMethod($queue, 'getExchangeType')); $this->assertSame(AMQPExchangeType::DIRECT, $this->callMethod($queue, 'getExchangeType', [''])); @@ -81,7 +76,6 @@ public function testExchangeType(): void public function testExchange(): void { - /** @var $queue RabbitMQQueue */ $queue = $this->connection(); $this->assertSame('test', $this->callMethod($queue, 'getExchange', ['test'])); $this->assertNull($this->callMethod($queue, 'getExchange', [''])); @@ -97,7 +91,6 @@ public function testExchange(): void public function testFailedExchange(): void { - /** @var $queue RabbitMQQueue */ $queue = $this->connection(); $this->assertSame('test', $this->callMethod($queue, 'getFailedExchange', ['test'])); $this->assertNull($this->callMethod($queue, 'getExchange', [''])); @@ -113,7 +106,6 @@ public function testFailedExchange(): void public function testRoutingKey(): void { - /** @var $queue RabbitMQQueue */ $queue = $this->connection(); $this->assertSame('test', $this->callMethod($queue, 'getRoutingKey', ['test'])); $this->assertSame('', $this->callMethod($queue, 'getRoutingKey', [''])); @@ -127,8 +119,8 @@ public function testRoutingKey(): void public function testFailedRoutingKey(): void { - /** @var $queue RabbitMQQueue */ $queue = $this->connection(); + $this->assertSame('test.failed', $this->callMethod($queue, 'getFailedRoutingKey', ['test'])); $this->assertSame('failed', $this->callMethod($queue, 'getFailedRoutingKey', [''])); @@ -139,25 +131,23 @@ public function testFailedRoutingKey(): void $this->assertSame('test.failed', $this->callMethod($queue, 'getFailedRoutingKey', ['test'])); } - public function testQuorum(): void + public function testConfigQuorum(): void { - /** @var $queue RabbitMQQueue */ - $queue = $this->connection(); - $this->assertFalse($this->callMethod($queue, 'isQuorum')); + $config = $this->connection()->getConfig(); + $this->assertFalse($this->callMethod($config, 'isQuorum')); - $queue = $this->connection('rabbitmq-with-options'); - $this->assertFalse($this->callMethod($queue, 'isQuorum')); + $config = $this->connection('rabbitmq-with-options')->getConfig(); + $this->assertFalse($this->callMethod($config, 'isQuorum')); - $queue = $this->connection('rabbitmq-with-quorum-options'); - $this->assertTrue($this->callMethod($queue, 'isQuorum')); + $config = $this->connection('rabbitmq-with-options-empty')->getConfig(); + $this->assertFalse($this->callMethod($config, 'isQuorum')); - $queue = $this->connection('rabbitmq-with-options-empty'); - $this->assertFalse($this->callMethod($queue, 'isQuorum')); + $config = $this->connection('rabbitmq-with-quorum-options')->getConfig(); + $this->assertTrue($this->callMethod($config, 'isQuorum')); } public function testDeclareDeleteExchange(): void { - /** @var $queue RabbitMQQueue */ $queue = $this->connection(); $name = Str::random(); @@ -173,7 +163,6 @@ public function testDeclareDeleteExchange(): void public function testDeclareDeleteQueue(): void { - /** @var $queue RabbitMQQueue */ $queue = $this->connection(); $name = Str::random(); @@ -191,7 +180,6 @@ public function testQueueArguments(): void { $name = Str::random(); - /** @var $queue RabbitMQQueue */ $queue = $this->connection(); $actual = $this->callMethod($queue, 'getQueueArguments', [$name]); $expected = []; @@ -233,7 +221,6 @@ public function testDelayQueueArguments(): void $name = Str::random(); $ttl = 12000; - /** @var $queue RabbitMQQueue */ $queue = $this->connection(); $actual = $this->callMethod($queue, 'getDelayQueueArguments', [$name, $ttl]); $expected = [ From 3a32d71a9b8fc5ad2934c2b3a8ff71d3d636c33c Mon Sep 17 00:00:00 2001 From: Emiel Bom Date: Thu, 2 Mar 2023 19:51:48 +0100 Subject: [PATCH 259/375] fixed: static factory call --- src/Queue/Connectors/RabbitMQConnector.php | 5 +---- src/Queue/QueueFactory.php | 17 ++++++----------- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/src/Queue/Connectors/RabbitMQConnector.php b/src/Queue/Connectors/RabbitMQConnector.php index 8d8498b4..53751c8b 100644 --- a/src/Queue/Connectors/RabbitMQConnector.php +++ b/src/Queue/Connectors/RabbitMQConnector.php @@ -20,12 +20,9 @@ class RabbitMQConnector implements ConnectorInterface { private Dispatcher $dispatcher; - private QueueFactory $queue; - public function __construct(Dispatcher $dispatcher) { $this->dispatcher = $dispatcher; - $this->queue = new QueueFactory(); } /** @@ -40,7 +37,7 @@ public function connect(array $config): Queue // Todo Create ConnectionFactory removing all deprecated dependicies $connection = $this->createConnection(Arr::except($config, 'options.queue')); - $queue = $this->queue->make($config)->setConnection($connection); + $queue = QueueFactory::make($config)->setConnection($connection); if ($queue instanceof HorizonRabbitMQQueue) { $this->dispatcher->listen(JobFailed::class, RabbitMQFailedEvent::class); diff --git a/src/Queue/QueueFactory.php b/src/Queue/QueueFactory.php index ccbfd51d..2595d8a4 100644 --- a/src/Queue/QueueFactory.php +++ b/src/Queue/QueueFactory.php @@ -7,31 +7,26 @@ final class QueueFactory { - protected $queue; - - /** - * Create a Queue - */ - public function make(array $config = []): RabbitMQQueue + public static function make(array $config = []): RabbitMQQueue { - $queueConfig = $this->createQueueConfig($config); + $queueConfig = self::createQueueConfig($config); $worker = Arr::get($config, 'worker', 'default'); if (strtolower($worker) == 'default') { - return $this->queue = new RabbitMQQueue($queueConfig); + return new RabbitMQQueue($queueConfig); } if (strtolower($worker) == 'horizon') { - return $this->queue = new HorizonRabbitMQQueue($queueConfig); + return new HorizonRabbitMQQueue($queueConfig); } - return $this->queue = new $worker($queueConfig); + return new $worker($queueConfig); } /** * Create a config object from config array */ - private function createQueueConfig(array $config = []): QueueConfig + private static function createQueueConfig(array $config = []): QueueConfig { return tap(new QueueConfig(), function (QueueConfig $queueConfig) use ($config) { if (! empty($queue = Arr::get($config, 'queue'))) { From 019a41138c6c869c8e31b1a211294e5f4bc53e5d Mon Sep 17 00:00:00 2001 From: Emiel Bom Date: Thu, 2 Mar 2023 21:39:04 +0100 Subject: [PATCH 260/375] fixed: - reverted config tests - private config getter --- src/Queue/RabbitMQQueue.php | 2 +- tests/Functional/RabbitMQQueueTest.php | 58 +++++++++++++------------- tests/Functional/TestCase.php | 20 ++++++++- 3 files changed, 49 insertions(+), 31 deletions(-) diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index 59d98f3a..22cced8b 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -709,7 +709,7 @@ protected function publishProperties($queue, array $options = []): array return [$destination, $exchange, $exchangeType, $attempts]; } - public function getConfig(): QueueConfig + private function getConfig(): QueueConfig { return $this->config; } diff --git a/tests/Functional/RabbitMQQueueTest.php b/tests/Functional/RabbitMQQueueTest.php index 535e6f74..5a50342b 100644 --- a/tests/Functional/RabbitMQQueueTest.php +++ b/tests/Functional/RabbitMQQueueTest.php @@ -23,41 +23,41 @@ public function testConnection(): void public function testConfigRerouteFailed(): void { - $config = $this->connection()->getConfig(); - $this->assertFalse($this->callMethod($config, 'isRerouteFailed')); + $queue = $this->connection(); + $this->assertFalse($this->callProperty($queue, 'config')->isRerouteFailed()); - $config = $this->connection('rabbitmq-with-options')->getConfig(); - $this->assertTrue($this->callMethod($config, 'isRerouteFailed')); + $queue = $this->connection('rabbitmq-with-options'); + $this->assertTrue($this->callProperty($queue, 'config')->isRerouteFailed()); - $config = $this->connection('rabbitmq-with-options-empty')->getConfig(); - $this->assertFalse($this->callMethod($config, 'isRerouteFailed')); + $queue = $this->connection('rabbitmq-with-options-empty'); + $this->assertFalse($this->callProperty($queue, 'config')->isRerouteFailed()); } public function testConfigPrioritizeDelayed(): void { - $config = $this->connection()->getConfig(); - $this->assertFalse($this->callMethod($config, 'isPrioritizeDelayed')); + $queue = $this->connection(); + $this->assertFalse($this->callProperty($queue, 'config')->isPrioritizeDelayed()); - $config = $this->connection('rabbitmq-with-options')->getConfig(); - $this->assertTrue($this->callMethod($config, 'isPrioritizeDelayed')); + $queue = $this->connection('rabbitmq-with-options'); + $this->assertTrue($this->callProperty($queue, 'config')->isPrioritizeDelayed()); - $config = $this->connection('rabbitmq-with-options-empty')->getConfig(); - $this->assertFalse($this->callMethod($config, 'isPrioritizeDelayed')); + $queue = $this->connection('rabbitmq-with-options-empty'); + $this->assertFalse($this->callProperty($queue, 'config')->isPrioritizeDelayed()); } public function testQueueMaxPriority(): void { - $config = $this->connection()->getConfig(); - $this->assertIsInt($this->callMethod($config, 'getQueueMaxPriority')); - $this->assertSame(2, $this->callMethod($config, 'getQueueMaxPriority')); + $queue = $this->connection(); + $this->assertIsInt($this->callProperty($queue, 'config')->getQueueMaxPriority()); + $this->assertSame(2, $this->callProperty($queue, 'config')->getQueueMaxPriority()); - $config = $this->connection('rabbitmq-with-options')->getConfig(); - $this->assertIsInt($this->callMethod($config, 'getQueueMaxPriority')); - $this->assertSame(20, $this->callMethod($config, 'getQueueMaxPriority')); + $queue = $this->connection('rabbitmq-with-options'); + $this->assertIsInt($this->callProperty($queue, 'config')->getQueueMaxPriority()); + $this->assertSame(20, $this->callProperty($queue, 'config')->getQueueMaxPriority()); - $config = $this->connection('rabbitmq-with-options-empty')->getConfig(); - $this->assertIsInt($this->callMethod($config, 'getQueueMaxPriority')); - $this->assertSame(2, $this->callMethod($config, 'getQueueMaxPriority')); + $queue = $this->connection('rabbitmq-with-options-empty'); + $this->assertIsInt($this->callProperty($queue, 'config')->getQueueMaxPriority()); + $this->assertSame(2, $this->callProperty($queue, 'config')->getQueueMaxPriority()); } public function testConfigExchangeType(): void @@ -133,17 +133,17 @@ public function testFailedRoutingKey(): void public function testConfigQuorum(): void { - $config = $this->connection()->getConfig(); - $this->assertFalse($this->callMethod($config, 'isQuorum')); + $queue = $this->connection(); + $this->assertFalse($this->callProperty($queue, 'config')->isQuorum()); - $config = $this->connection('rabbitmq-with-options')->getConfig(); - $this->assertFalse($this->callMethod($config, 'isQuorum')); + $queue = $this->connection('rabbitmq-with-options'); + $this->assertFalse($this->callProperty($queue, 'config')->isQuorum()); - $config = $this->connection('rabbitmq-with-options-empty')->getConfig(); - $this->assertFalse($this->callMethod($config, 'isQuorum')); + $queue = $this->connection('rabbitmq-with-options-empty'); + $this->assertFalse($this->callProperty($queue, 'config')->isQuorum()); - $config = $this->connection('rabbitmq-with-quorum-options')->getConfig(); - $this->assertTrue($this->callMethod($config, 'isQuorum')); + $queue = $this->connection('rabbitmq-with-quorum-options'); + $this->assertTrue($this->callProperty($queue, 'config')->isQuorum()); } public function testDeclareDeleteExchange(): void diff --git a/tests/Functional/TestCase.php b/tests/Functional/TestCase.php index 01b3c069..1d3069f1 100644 --- a/tests/Functional/TestCase.php +++ b/tests/Functional/TestCase.php @@ -165,7 +165,7 @@ protected function getEnvironmentSetUp($app): void * * @throws Exception */ - protected function callMethod($object, string $method, array $parameters = []) + protected function callMethod($object, string $method, array $parameters = []): mixed { try { $className = get_class($object); @@ -179,4 +179,22 @@ protected function callMethod($object, string $method, array $parameters = []) return $method->invokeArgs($object, $parameters); } + + /** + * @throws Exception + */ + protected function callProperty($object, string $property): mixed + { + try { + $className = get_class($object); + $reflection = new ReflectionClass($className); + } catch (ReflectionException $e) { + throw new Exception($e->getMessage()); + } + + $property = $reflection->getProperty($property); + $property->setAccessible(true); + + return $property->getValue($object); + } } From aee2a4241e187086ae622a9058b8a432f6d6b45d Mon Sep 17 00:00:00 2001 From: Emiel Bom Date: Fri, 3 Mar 2023 10:58:25 +0100 Subject: [PATCH 261/375] fixed: - phpdoc tags --- tests/Functional/TestCase.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/Functional/TestCase.php b/tests/Functional/TestCase.php index 1d3069f1..5ffa54d9 100644 --- a/tests/Functional/TestCase.php +++ b/tests/Functional/TestCase.php @@ -161,8 +161,6 @@ protected function getEnvironmentSetUp($app): void } /** - * @return mixed - * * @throws Exception */ protected function callMethod($object, string $method, array $parameters = []): mixed From 3cee3ffd6d5f596d799152e4aa466bd91c5c09f9 Mon Sep 17 00:00:00 2001 From: Emiel Bom Date: Thu, 9 Mar 2023 18:50:14 +0100 Subject: [PATCH 262/375] added: - factory for making a connection. - factory for creating a config object. - test lazy stream connection. fixed: - tests removed: - unnecessary methods --- config/rabbitmq.php | 10 +- src/Queue/Connection/ConfigFactory.php | 93 +++++++++ src/Queue/Connection/ConnectionFactory.php | 224 +++++++++++++++++++++ src/Queue/Connectors/RabbitMQConnector.php | 47 +---- tests/Feature/ConnectorTest.php | 43 ++++ tests/Feature/QueueTest.php | 4 +- tests/Functional/TestCase.php | 9 +- 7 files changed, 369 insertions(+), 61 deletions(-) create mode 100644 src/Queue/Connection/ConfigFactory.php create mode 100644 src/Queue/Connection/ConnectionFactory.php diff --git a/config/rabbitmq.php b/config/rabbitmq.php index 900e3b74..4c102ce8 100644 --- a/config/rabbitmq.php +++ b/config/rabbitmq.php @@ -9,7 +9,7 @@ 'driver' => 'rabbitmq', 'queue' => env('RABBITMQ_QUEUE', 'default'), - 'connection' => PhpAmqpLib\Connection\AMQPLazyConnection::class, + 'connection' => 'default', 'hosts' => [ [ @@ -22,14 +22,6 @@ ], 'options' => [ - 'ssl_options' => [ - 'cafile' => env('RABBITMQ_SSL_CAFILE', null), - 'local_cert' => env('RABBITMQ_SSL_LOCALCERT', null), - 'local_key' => env('RABBITMQ_SSL_LOCALKEY', null), - 'verify_peer' => env('RABBITMQ_SSL_VERIFY_PEER', true), - 'passphrase' => env('RABBITMQ_SSL_PASSPHRASE', null), - ], - 'queue' => [], ], /* diff --git a/src/Queue/Connection/ConfigFactory.php b/src/Queue/Connection/ConfigFactory.php new file mode 100644 index 00000000..3e5e0c14 --- /dev/null +++ b/src/Queue/Connection/ConfigFactory.php @@ -0,0 +1,93 @@ +setIsLazy(! in_array( + Arr::get($config, 'lazy') ?? true, + [false, 0, '0', 'false'], + true) + ); + + // Set the connection to unsecure by default + $connectionConfig->setIsSecure(in_array( + Arr::get($config, 'secure'), + [true, 1, '1', 'true'], + true) + ); + + if ($connectionConfig->isSecure()) { + self::sllOptionsFromConfig($connectionConfig, $config); + } + + self::hostFromConfig($connectionConfig, $config); + self::heartbeatFromConfig($connectionConfig, $config); + }); + } + + protected static function hostFromConfig(AMQPConnectionConfig $connectionConfig, array $config): void + { + $hostConfig = Arr::first(Arr::shuffle(Arr::get($config, self::CONFIG_HOSTS, [])), null, []); + + if ($location = Arr::get($hostConfig, 'host')) { + $connectionConfig->setHost($location); + } + if ($port = Arr::get($hostConfig, 'port')) { + $connectionConfig->setPort($port); + } + if ($vhost = Arr::get($hostConfig, 'vhost')) { + $connectionConfig->setVhost($vhost); + } + if ($user = Arr::get($hostConfig, 'user')) { + $connectionConfig->setUser($user); + } + if ($password = Arr::get($hostConfig, 'password')) { + $connectionConfig->setPassword($password); + } + } + + protected static function sllOptionsFromConfig(AMQPConnectionConfig $connectionConfig, array $config): void + { + $sslConfig = Arr::get($config, self::CONFIG_OPTIONS.'.ssl_options', []); + + if ($caFile = Arr::get($sslConfig, 'cafile')) { + $connectionConfig->setSslCaCert($caFile); + } + if ($cert = Arr::get($sslConfig, 'local_cert')) { + $connectionConfig->setSslCert($cert); + } + if ($key = Arr::get($sslConfig, 'local_key')) { + $connectionConfig->setSslKey($key); + } + if ($verifyPeer = Arr::get($sslConfig, 'verify_peer')) { + $connectionConfig->setSslVerify($verifyPeer); + } + if ($passphrase = Arr::get($sslConfig, 'passphrase')) { + $connectionConfig->setSslPassPhrase($passphrase); + } + } + + protected static function heartbeatFromConfig(AMQPConnectionConfig $connectionConfig, array $config): void + { + $heartbeat = Arr::get($config, self::CONFIG_OPTIONS.'.heartbeat'); + + if (! empty($heartbeat) && is_numeric($heartbeat) && 0 < (int) $heartbeat) { + $connectionConfig->setHeartbeat($heartbeat); + } + } +} diff --git a/src/Queue/Connection/ConnectionFactory.php b/src/Queue/Connection/ConnectionFactory.php new file mode 100644 index 00000000..3b5c0fe1 --- /dev/null +++ b/src/Queue/Connection/ConnectionFactory.php @@ -0,0 +1,224 @@ +getIoType() === AMQPConnectionConfig::IO_TYPE_SOCKET) { + return self::createSocketConnection($connection, $config); + } + + return self::createStreamConnection($connection, $config); + } + + private static function createSocketConnection($connection, AMQPConnectionConfig $config): AMQPSocketConnection + { + self::assertSocketConnection($connection, $config); + + return new $connection( + $config->getHost(), + $config->getPort(), + $config->getUser(), + $config->getPassword(), + $config->getVhost(), + $config->isInsist(), + $config->getLoginMethod(), + $config->getLoginResponse(), + $config->getLocale(), + $config->getReadTimeout(), + $config->isKeepalive(), + $config->getWriteTimeout(), + $config->getHeartbeat(), + $config->getChannelRPCTimeout(), + $config + ); + } + + private static function createStreamConnection($connection, AMQPConnectionConfig $config): AMQPStreamConnection + { + self::assertStreamConnection($connection); + + if ($config->isSecure()) { + self::assertSSLConnection($connection); + + return new $connection( + $config->getHost(), + $config->getPort(), + $config->getUser(), + $config->getPassword(), + $config->getVhost(), + self::getSslOptions($config), + [ + 'insist' => $config->isInsist(), + 'login_method' => $config->getLoginMethod(), + 'login_response' => $config->getLoginResponse(), + 'locale' => $config->getLocale(), + 'connection_timeout' => $config->getConnectionTimeout(), + 'read_write_timeout' => self::getReadWriteTimeout($config), + 'keepalive' => $config->isKeepalive(), + 'heartbeat' => $config->getHeartbeat(), + ], + $config->getNetworkProtocol(), + $config + ); + } + + return new $connection( + $config->getHost(), + $config->getPort(), + $config->getUser(), + $config->getPassword(), + $config->getVhost(), + $config->isInsist(), + $config->getLoginMethod(), + $config->getLoginResponse(), + $config->getLocale(), + $config->getConnectionTimeout(), + self::getReadWriteTimeout($config), + $config->getStreamContext(), + $config->isKeepalive(), + $config->getHeartbeat(), + $config->getChannelRPCTimeout(), + $config->getNetworkProtocol(), + $config + ); + } + + protected static function getReadWriteTimeout(AMQPConnectionConfig $config): float + { + return min($config->getReadTimeout(), $config->getWriteTimeout()); + } + + private static function getSslOptions(AMQPConnectionConfig $config): array + { + return array_filter([ + 'cafile' => $config->getSslCaCert(), + 'capath' => $config->getSslCaPath(), + 'local_cert' => $config->getSslCert(), + 'local_pk' => $config->getSslKey(), + 'verify_peer' => $config->getSslVerify(), + 'verify_peer_name' => $config->getSslVerifyName(), + 'passphrase' => $config->getSslPassPhrase(), + 'ciphers' => $config->getSslCiphers(), + 'security_level' => $config->getSslSecurityLevel(), + ], static function ($value) { + return null !== $value; + }); + } + + private static function assertConnectionFromConfig(string $connection): void + { + if ($connection !== self::CONNECTION_TYPE_DEFAULT && ! is_subclass_of($connection, self::CONNECTION_TYPE_EXTENDED)) { + throw new AMQPLogicException(sprintf('The config property \'%s\' must contain \'%s\' or must extend: %s', self::CONFIG_CONNECTION, self::CONNECTION_TYPE_DEFAULT, class_basename(self::CONNECTION_TYPE_EXTENDED))); + } + } + + private static function assertSocketConnection($connection, AMQPConnectionConfig $config): void + { + self::assertExtendedOf($connection, self::CONNECTION_SUB_TYPE_SOCKET); + + if ($config->isSecure()) { + throw new AMQPLogicException('The socket connection implementation does not support secure connections.'); + } + } + + private static function assertStreamConnection($connection): void + { + self::assertExtendedOf($connection, self::CONNECTION_SUB_TYPE_STREAM); + } + + private static function assertSSLConnection($connection) + { + self::assertExtendedOf($connection, self::CONNECTION_SUB_TYPE_SSL); + } + + private static function assertExtendedOf($connection, string $abstract): void + { + if (! is_subclass_of($connection, $abstract)) { + throw new AMQPLogicException(sprintf('The connection must extend: %s', class_basename($abstract))); + } + } + + /** + * @return mixed + * + * @throws Exception + * + * @deprecated This is the fallback method, update your config asap. (example: connection => 'default') + */ + private static function _createLazyConnection($connection, array $config): AbstractConnection + { + return $connection::create_connection( + Arr::shuffle(Arr::get($config, ConfigFactory::CONFIG_HOSTS, [])), + Arr::add(Arr::get($config, 'options', []), 'heartbeat', 0) + ); + } +} diff --git a/src/Queue/Connectors/RabbitMQConnector.php b/src/Queue/Connectors/RabbitMQConnector.php index 53751c8b..97c964c1 100644 --- a/src/Queue/Connectors/RabbitMQConnector.php +++ b/src/Queue/Connectors/RabbitMQConnector.php @@ -8,11 +8,9 @@ use Illuminate\Queue\Connectors\ConnectorInterface; use Illuminate\Queue\Events\JobFailed; use Illuminate\Queue\Events\WorkerStopping; -use Illuminate\Support\Arr; -use PhpAmqpLib\Connection\AbstractConnection; -use PhpAmqpLib\Connection\AMQPLazyConnection; use VladimirYuldashev\LaravelQueueRabbitMQ\Horizon\Listeners\RabbitMQFailedEvent; use VladimirYuldashev\LaravelQueueRabbitMQ\Horizon\RabbitMQQueue as HorizonRabbitMQQueue; +use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Connection\ConnectionFactory; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\QueueFactory; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue; @@ -34,8 +32,7 @@ public function __construct(Dispatcher $dispatcher) */ public function connect(array $config): Queue { - // Todo Create ConnectionFactory removing all deprecated dependicies - $connection = $this->createConnection(Arr::except($config, 'options.queue')); + $connection = ConnectionFactory::make($config); $queue = QueueFactory::make($config)->setConnection($connection); @@ -49,44 +46,4 @@ public function connect(array $config): Queue return $queue; } - - /** - * @throws Exception - */ - protected function createConnection(array $config): AbstractConnection - { - /** @var AbstractConnection $connection */ - $connection = Arr::get($config, 'connection', AMQPLazyConnection::class); - - // disable heartbeat when not configured, so long-running tasks will not fail - $config = Arr::add($config, 'options.heartbeat', 0); - - return $connection::create_connection( - Arr::shuffle(Arr::get($config, 'hosts', [])), - $this->filter(Arr::get($config, 'options', [])) - ); - } - - /** - * Recursively filter only null values. - */ - private function filter(array $array): array - { - foreach ($array as $index => &$value) { - if (is_array($value)) { - $value = $this->filter($value); - - continue; - } - - // If the value is null then remove it. - if ($value === null) { - unset($array[$index]); - - continue; - } - } - - return $array; - } } diff --git a/tests/Feature/ConnectorTest.php b/tests/Feature/ConnectorTest.php index 499018e9..00cf5d6e 100644 --- a/tests/Feature/ConnectorTest.php +++ b/tests/Feature/ConnectorTest.php @@ -5,6 +5,7 @@ use Illuminate\Queue\QueueManager; use PhpAmqpLib\Connection\AMQPLazyConnection; use PhpAmqpLib\Connection\AMQPSSLConnection; +use PhpAmqpLib\Connection\AMQPStreamConnection; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue; class ConnectorTest extends \VladimirYuldashev\LaravelQueueRabbitMQ\Tests\TestCase @@ -51,6 +52,48 @@ public function testLazyConnection(): void $this->assertTrue($connection->getChannel()->is_open()); } + public function testLazyStreamConnection(): void + { + $this->app['config']->set('queue.connections.rabbitmq', [ + 'driver' => 'rabbitmq', + 'queue' => env('RABBITMQ_QUEUE', 'default'), + 'connection' => 'default', + + 'hosts' => [ + [ + 'host' => getenv('HOST'), + 'port' => getenv('PORT'), + 'user' => 'guest', + 'password' => 'guest', + 'vhost' => '/', + ], + ], + + 'options' => [ + 'ssl_options' => [ + 'cafile' => env('RABBITMQ_SSL_CAFILE', null), + 'local_cert' => env('RABBITMQ_SSL_LOCALCERT', null), + 'local_key' => env('RABBITMQ_SSL_LOCALKEY', null), + 'verify_peer' => env('RABBITMQ_SSL_VERIFY_PEER', true), + 'passphrase' => env('RABBITMQ_SSL_PASSPHRASE', null), + ], + ], + + 'worker' => env('RABBITMQ_WORKER', 'default'), + ]); + + /** @var QueueManager $queue */ + $queue = $this->app['queue']; + + /** @var RabbitMQQueue $connection */ + $connection = $queue->connection('rabbitmq'); + + $this->assertInstanceOf(RabbitMQQueue::class, $connection); + $this->assertInstanceOf(AMQPStreamConnection::class, $connection->getConnection()); + $this->assertTrue($connection->getConnection()->isConnected()); + $this->assertTrue($connection->getChannel()->is_open()); + } + public function testSslConnection(): void { $this->markTestSkipped(); diff --git a/tests/Feature/QueueTest.php b/tests/Feature/QueueTest.php index 15b8acbb..3258b7f2 100644 --- a/tests/Feature/QueueTest.php +++ b/tests/Feature/QueueTest.php @@ -2,12 +2,12 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Feature; -use PhpAmqpLib\Connection\AMQPLazyConnection; +use PhpAmqpLib\Connection\AMQPStreamConnection; class QueueTest extends TestCase { public function testConnection(): void { - $this->assertInstanceOf(AMQPLazyConnection::class, $this->connection()->getChannel()->getConnection()); + $this->assertInstanceOf(AMQPStreamConnection::class, $this->connection()->getChannel()->getConnection()); } } diff --git a/tests/Functional/TestCase.php b/tests/Functional/TestCase.php index 5ffa54d9..4edc8853 100644 --- a/tests/Functional/TestCase.php +++ b/tests/Functional/TestCase.php @@ -3,7 +3,6 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Functional; use Exception; -use PhpAmqpLib\Connection\AMQPLazyConnection; use ReflectionClass; use ReflectionException; use VladimirYuldashev\LaravelQueueRabbitMQ\Tests\TestCase as BaseTestCase; @@ -16,7 +15,7 @@ protected function getEnvironmentSetUp($app): void $app['config']->set('queue.connections.rabbitmq', [ 'driver' => 'rabbitmq', 'queue' => 'order', - 'connection' => AMQPLazyConnection::class, + 'connection' => 'default', 'hosts' => [ [ @@ -44,7 +43,7 @@ protected function getEnvironmentSetUp($app): void $app['config']->set('queue.connections.rabbitmq-with-options', [ 'driver' => 'rabbitmq', 'queue' => 'order', - 'connection' => AMQPLazyConnection::class, + 'connection' => 'default', 'hosts' => [ [ @@ -83,7 +82,7 @@ protected function getEnvironmentSetUp($app): void $app['config']->set('queue.connections.rabbitmq-with-options-empty', [ 'driver' => 'rabbitmq', 'queue' => 'order', - 'connection' => AMQPLazyConnection::class, + 'connection' => 'default', 'hosts' => [ [ @@ -123,7 +122,7 @@ protected function getEnvironmentSetUp($app): void $app['config']->set('queue.connections.rabbitmq-with-quorum-options', [ 'driver' => 'rabbitmq', 'queue' => 'order', - 'connection' => AMQPLazyConnection::class, + 'connection' => 'default', 'hosts' => [ [ From 3edec270c6fd5e22df42c5f51a0115276f41f579 Mon Sep 17 00:00:00 2001 From: Emiel Bom Date: Thu, 9 Mar 2023 18:50:47 +0100 Subject: [PATCH 263/375] documented connection options and config --- README.md | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 24c25862..d425e77b 100644 --- a/README.md +++ b/README.md @@ -254,9 +254,83 @@ class RabbitMQJob extends BaseJob } ``` +### SSL Secure + +When u need a secure connection to rabbitMQ server(s). Then this is posible by settitng extra config options. + +```php +'connections' => [ + // ... + + 'rabbitmq' => [ + // ... + + 'secure' = > true, + 'options' => [ + // ... + + 'ssl_options' => [ + 'cafile' => env('RABBITMQ_SSL_CAFILE', null), + 'local_cert' => env('RABBITMQ_SSL_LOCALCERT', null), + 'local_key' => env('RABBITMQ_SSL_LOCALKEY', null), + 'verify_peer' => env('RABBITMQ_SSL_VERIFY_PEER', true), + 'passphrase' => env('RABBITMQ_SSL_PASSPHRASE', null), + ], + ], + ], + + // ... +], +``` + +### Lazy Connection + +By default, your connection will be created as a lazy connection. +If for some reason you dont want the connection lazy you can turn it off by setting the following config. + +```php +'connections' => [ + // ... + + 'rabbitmq' => [ + // ... + + 'lazy' = > false, + ], + + // ... +], +``` + +### Heartbeat + +By default, your connection will be created with a heartbeat setting of `0`. +You can alter the heartbeat settings by changing the config. + +```php + +'connections' => [ + // ... + + 'rabbitmq' => [ + // ... + + 'secure' = > true, + 'options' => [ + // ... + + 'heartbeat' => 10, + ], + ], + + // ... +], +``` + ## Laravel Usage -Once you completed the configuration you can use the Laravel Queue API. If you used other queue drivers you do not need to +Once you completed the configuration you can use the Laravel Queue API. If you used other queue drivers you do not need +to change anything else. If you do not know how to use the Queue API, please refer to the official Laravel documentation: http://laravel.com/docs/queues From 6dd098f4c9edabd177322a553bf8801e646a4f55 Mon Sep 17 00:00:00 2001 From: Emiel Bom Date: Thu, 9 Mar 2023 19:03:12 +0100 Subject: [PATCH 264/375] small code style fix --- src/Queue/Connection/ConnectionFactory.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Queue/Connection/ConnectionFactory.php b/src/Queue/Connection/ConnectionFactory.php index 3b5c0fe1..d9879dbe 100644 --- a/src/Queue/Connection/ConnectionFactory.php +++ b/src/Queue/Connection/ConnectionFactory.php @@ -195,7 +195,7 @@ private static function assertStreamConnection($connection): void self::assertExtendedOf($connection, self::CONNECTION_SUB_TYPE_STREAM); } - private static function assertSSLConnection($connection) + private static function assertSSLConnection($connection): void { self::assertExtendedOf($connection, self::CONNECTION_SUB_TYPE_SSL); } From 92a1ef44ad2cbf88a31689cd644eb81dd7b63734 Mon Sep 17 00:00:00 2001 From: Emiel Bom Date: Thu, 9 Mar 2023 19:24:55 +0100 Subject: [PATCH 265/375] update php-amqplib --- composer.json | 112 +++++++++++++++++++++++++------------------------- 1 file changed, 56 insertions(+), 56 deletions(-) diff --git a/composer.json b/composer.json index 5461314d..2238a6b3 100644 --- a/composer.json +++ b/composer.json @@ -1,59 +1,59 @@ { - "name": "vladimir-yuldashev/laravel-queue-rabbitmq", - "description": "RabbitMQ driver for Laravel Queue. Supports Laravel Horizon.", - "license": "MIT", - "authors": [ - { - "name": "Vladimir Yuldashev", - "email": "misterio92@gmail.com" - } - ], - "require": { - "php": "^8.0", - "ext-json": "*", - "illuminate/queue": "^9.0|^10.0", - "php-amqplib/php-amqplib": "^3.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "mockery/mockery": "^1.0", - "laravel/horizon": "^5.0", - "orchestra/testbench": "^7.0|^8.0", - "laravel/pint": "^1.2", - "laravel/framework": "^9.0|^10.0" - }, - "autoload": { - "psr-4": { - "VladimirYuldashev\\LaravelQueueRabbitMQ\\": "src/" - } - }, - "autoload-dev": { - "psr-4": { - "VladimirYuldashev\\LaravelQueueRabbitMQ\\Tests\\": "tests/" - } - }, - "extra": { - "branch-alias": { - "dev-master": "13.0-dev" - }, - "laravel": { - "providers": [ - "VladimirYuldashev\\LaravelQueueRabbitMQ\\LaravelQueueRabbitMQServiceProvider" - ] - } - }, - "suggest": { - "ext-pcntl": "Required to use all features of the queue consumer." - }, - "scripts": { - "test": [ - "@test:style", - "@test:unit" + "name": "vladimir-yuldashev/laravel-queue-rabbitmq", + "description": "RabbitMQ driver for Laravel Queue. Supports Laravel Horizon.", + "license": "MIT", + "authors": [ + { + "name": "Vladimir Yuldashev", + "email": "misterio92@gmail.com" + } ], - "test:style": "@php vendor/bin/php-cs-fixer fix --config=.php-cs-fixer.dist.php --allow-risky=yes --dry-run --diff --verbose", - "test:unit": "@php vendor/bin/phpunit", - "fix:style": "@php vendor/bin/php-cs-fixer fix --config=.php-cs-fixer.dist.php --allow-risky=yes --diff --verbose" - }, - "minimum-stability": "dev", - "prefer-stable": true + "require": { + "php": "^8.0", + "ext-json": "*", + "illuminate/queue": "^9.0|^10.0", + "php-amqplib/php-amqplib": "^v3.5" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "mockery/mockery": "^1.0", + "laravel/horizon": "^5.0", + "orchestra/testbench": "^7.0|^8.0", + "laravel/pint": "^1.2", + "laravel/framework": "^9.0|^10.0" + }, + "autoload": { + "psr-4": { + "VladimirYuldashev\\LaravelQueueRabbitMQ\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "VladimirYuldashev\\LaravelQueueRabbitMQ\\Tests\\": "tests/" + } + }, + "extra": { + "branch-alias": { + "dev-master": "13.0-dev" + }, + "laravel": { + "providers": [ + "VladimirYuldashev\\LaravelQueueRabbitMQ\\LaravelQueueRabbitMQServiceProvider" + ] + } + }, + "suggest": { + "ext-pcntl": "Required to use all features of the queue consumer." + }, + "scripts": { + "test": [ + "@test:style", + "@test:unit" + ], + "test:style": "@php vendor/bin/php-cs-fixer fix --config=.php-cs-fixer.dist.php --allow-risky=yes --dry-run --diff --verbose", + "test:unit": "@php vendor/bin/phpunit", + "fix:style": "@php vendor/bin/php-cs-fixer fix --config=.php-cs-fixer.dist.php --allow-risky=yes --diff --verbose" + }, + "minimum-stability": "dev", + "prefer-stable": true } From 222ddb404c983a8a0ba8565a9e03a4622bcec9f2 Mon Sep 17 00:00:00 2001 From: Emiel Bom Date: Sat, 11 Mar 2023 21:40:26 +0100 Subject: [PATCH 266/375] update php-amqplib - reverted to minimal requirement update --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 2238a6b3..c29cd7d2 100644 --- a/composer.json +++ b/composer.json @@ -12,7 +12,7 @@ "php": "^8.0", "ext-json": "*", "illuminate/queue": "^9.0|^10.0", - "php-amqplib/php-amqplib": "^v3.5" + "php-amqplib/php-amqplib": "^v3.2.0" }, "require-dev": { "phpunit/phpunit": "^9.3", From 502bb75e47712eadb9017aeede0a926e6bb3c343 Mon Sep 17 00:00:00 2001 From: Emiel Bom Date: Sat, 11 Mar 2023 21:40:42 +0100 Subject: [PATCH 267/375] update php-amqplib - reverted to minimal requirement update --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index c29cd7d2..94bfc356 100644 --- a/composer.json +++ b/composer.json @@ -12,7 +12,7 @@ "php": "^8.0", "ext-json": "*", "illuminate/queue": "^9.0|^10.0", - "php-amqplib/php-amqplib": "^v3.2.0" + "php-amqplib/php-amqplib": "^v3.2" }, "require-dev": { "phpunit/phpunit": "^9.3", From 61f9142d8a09a14626c68c7c71171382092295e3 Mon Sep 17 00:00:00 2001 From: Emiel Bom Date: Sat, 11 Mar 2023 22:00:33 +0100 Subject: [PATCH 268/375] added: - factory for creating a config object. fixed: - tests removed: - unnecessary methods --- src/Queue/QueueConfigFactory.php | 74 ++++++++++++++++++++++++++++++++ src/Queue/QueueFactory.php | 39 +---------------- 2 files changed, 76 insertions(+), 37 deletions(-) create mode 100644 src/Queue/QueueConfigFactory.php diff --git a/src/Queue/QueueConfigFactory.php b/src/Queue/QueueConfigFactory.php new file mode 100644 index 00000000..6266cf20 --- /dev/null +++ b/src/Queue/QueueConfigFactory.php @@ -0,0 +1,74 @@ +setQueue($queue); + } + if (! empty($afterCommit = Arr::get($config, 'after_commit'))) { + $queueConfig->setDispatchAfterCommit($afterCommit); + } + + self::getOptionsFromConfig($queueConfig, $config); + }); + } + + private static function getOptionsFromConfig(QueueConfig $queueConfig, array $config): void + { + $queueOptions = Arr::get($config, self::CONFIG_OPTIONS.'.queue', []) ?: []; + + if ($job = Arr::pull($queueOptions, 'job')) { + $queueConfig->setAbstractJob($job); + } + + // Feature: Prioritize delayed messages. + if ($prioritizeDelayed = Arr::pull($queueOptions, 'prioritize_delayed')) { + $queueConfig->setPrioritizeDelayed($prioritizeDelayed); + } + if ($maxPriority = Arr::pull($queueOptions, 'queue_max_priority')) { + $queueConfig->setQueueMaxPriority($maxPriority); + } + + // Feature: Working with Exchange and routing-keys + if ($exchange = Arr::pull($queueOptions, 'exchange')) { + $queueConfig->setExchange($exchange); + } + if ($exchangeType = Arr::pull($queueOptions, 'exchange_type')) { + $queueConfig->setExchangeType($exchangeType); + } + if ($exchangeRoutingKey = Arr::pull($queueOptions, 'exchange_routing_key')) { + $queueConfig->setExchangeRoutingKey($exchangeRoutingKey); + } + + // Feature: Reroute failed messages + if ($rerouteFailed = Arr::pull($queueOptions, 'reroute_failed')) { + $queueConfig->setRerouteFailed($rerouteFailed); + } + if ($failedExchange = Arr::pull($queueOptions, 'failed_exchange')) { + $queueConfig->setFailedExchange($failedExchange); + } + if ($failedRoutingKey = Arr::pull($queueOptions, 'failed_routing_key')) { + $queueConfig->setFailedRoutingKey($failedRoutingKey); + } + + // Feature: Mark queue as quorum + if ($quorum = Arr::pull($queueOptions, 'quorum')) { + $queueConfig->setQuorum($quorum); + } + + // All extra options not defined + $queueConfig->setOptions($queueOptions); + } +} diff --git a/src/Queue/QueueFactory.php b/src/Queue/QueueFactory.php index 2595d8a4..75bde1ed 100644 --- a/src/Queue/QueueFactory.php +++ b/src/Queue/QueueFactory.php @@ -5,11 +5,11 @@ use Illuminate\Support\Arr; use VladimirYuldashev\LaravelQueueRabbitMQ\Horizon\RabbitMQQueue as HorizonRabbitMQQueue; -final class QueueFactory +class QueueFactory { public static function make(array $config = []): RabbitMQQueue { - $queueConfig = self::createQueueConfig($config); + $queueConfig = QueueConfigFactory::make($config); $worker = Arr::get($config, 'worker', 'default'); if (strtolower($worker) == 'default') { @@ -22,39 +22,4 @@ public static function make(array $config = []): RabbitMQQueue return new $worker($queueConfig); } - - /** - * Create a config object from config array - */ - private static function createQueueConfig(array $config = []): QueueConfig - { - return tap(new QueueConfig(), function (QueueConfig $queueConfig) use ($config) { - if (! empty($queue = Arr::get($config, 'queue'))) { - $queueConfig->setQueue($queue); - } - if (! empty($afterCommit = Arr::get($config, 'after_commit'))) { - $queueConfig->setDispatchAfterCommit($afterCommit); - } - - if (! empty($queueOptionsConfig = Arr::get($config, 'options.queue'))) { - $queueConfig - ->setAbstractJob(Arr::pull($queueOptionsConfig, 'job')) - // Feature: Prioritize delayed messages. - ->setPrioritizeDelayed(Arr::pull($queueOptionsConfig, 'prioritize_delayed')) - ->setQueueMaxPriority(Arr::pull($queueOptionsConfig, 'queue_max_priority')) - // Feature: Working with Exchange and routing-keys - ->setExchange(Arr::pull($queueOptionsConfig, 'exchange')) - ->setExchangeType(Arr::pull($queueOptionsConfig, 'exchange_type')) - ->setExchangeRoutingKey(Arr::pull($queueOptionsConfig, 'exchange_routing_key')) - // Feature: Reroute failed messages - ->setRerouteFailed(Arr::pull($queueOptionsConfig, 'reroute_failed')) - ->setFailedExchange(Arr::pull($queueOptionsConfig, 'failed_exchange')) - ->setFailedRoutingKey(Arr::pull($queueOptionsConfig, 'failed_routing_key')) - // Feature: Mark queue as quorum - ->setQuorum(Arr::pull($queueOptionsConfig, 'quorum')) - // All extra options not defined - ->setOptions($queueOptionsConfig); - } - }); - } } From 859fedaedb6e5575068fcccc035801013f7169da Mon Sep 17 00:00:00 2001 From: Emiel Bom Date: Sat, 11 Mar 2023 22:01:28 +0100 Subject: [PATCH 269/375] cleanup --- src/Queue/Connection/ConfigFactory.php | 18 +++++++++--------- src/Queue/Connection/ConnectionFactory.php | 14 +++++++------- src/Queue/QueueConfig.php | 6 ++++-- 3 files changed, 20 insertions(+), 18 deletions(-) diff --git a/src/Queue/Connection/ConfigFactory.php b/src/Queue/Connection/ConfigFactory.php index 3e5e0c14..9e27529d 100644 --- a/src/Queue/Connection/ConfigFactory.php +++ b/src/Queue/Connection/ConfigFactory.php @@ -5,12 +5,12 @@ use Illuminate\Support\Arr; use PhpAmqpLib\Connection\AMQPConnectionConfig; -class ConfigFactory +final class ConfigFactory { - protected const CONFIG_OPTIONS = 'options'; - public const CONFIG_HOSTS = 'hosts'; + private const CONFIG_OPTIONS = 'options'; + /** * Create a config object from config array */ @@ -32,15 +32,15 @@ public static function make(array $config = []): AMQPConnectionConfig ); if ($connectionConfig->isSecure()) { - self::sllOptionsFromConfig($connectionConfig, $config); + self::getSLLOptionsFromConfig($connectionConfig, $config); } - self::hostFromConfig($connectionConfig, $config); - self::heartbeatFromConfig($connectionConfig, $config); + self::getHostFromConfig($connectionConfig, $config); + self::getHeartbeatFromConfig($connectionConfig, $config); }); } - protected static function hostFromConfig(AMQPConnectionConfig $connectionConfig, array $config): void + private static function getHostFromConfig(AMQPConnectionConfig $connectionConfig, array $config): void { $hostConfig = Arr::first(Arr::shuffle(Arr::get($config, self::CONFIG_HOSTS, [])), null, []); @@ -61,7 +61,7 @@ protected static function hostFromConfig(AMQPConnectionConfig $connectionConfig, } } - protected static function sllOptionsFromConfig(AMQPConnectionConfig $connectionConfig, array $config): void + private static function getSLLOptionsFromConfig(AMQPConnectionConfig $connectionConfig, array $config): void { $sslConfig = Arr::get($config, self::CONFIG_OPTIONS.'.ssl_options', []); @@ -82,7 +82,7 @@ protected static function sllOptionsFromConfig(AMQPConnectionConfig $connectionC } } - protected static function heartbeatFromConfig(AMQPConnectionConfig $connectionConfig, array $config): void + private static function getHeartbeatFromConfig(AMQPConnectionConfig $connectionConfig, array $config): void { $heartbeat = Arr::get($config, self::CONFIG_OPTIONS.'.heartbeat'); diff --git a/src/Queue/Connection/ConnectionFactory.php b/src/Queue/Connection/ConnectionFactory.php index d9879dbe..2ad99176 100644 --- a/src/Queue/Connection/ConnectionFactory.php +++ b/src/Queue/Connection/ConnectionFactory.php @@ -17,17 +17,17 @@ class ConnectionFactory { - protected const CONNECTION_TYPE_DEFAULT = 'default'; + private const CONNECTION_TYPE_DEFAULT = 'default'; - protected const CONNECTION_TYPE_EXTENDED = AbstractConnection::class; + private const CONNECTION_TYPE_EXTENDED = AbstractConnection::class; - protected const CONNECTION_SUB_TYPE_STREAM = AMQPStreamConnection::class; + private const CONNECTION_SUB_TYPE_STREAM = AMQPStreamConnection::class; - protected const CONNECTION_SUB_TYPE_SOCKET = AMQPSocketConnection::class; + private const CONNECTION_SUB_TYPE_SOCKET = AMQPSocketConnection::class; - protected const CONNECTION_SUB_TYPE_SSL = AMQPSSLConnection::class; + private const CONNECTION_SUB_TYPE_SSL = AMQPSSLConnection::class; - protected const CONFIG_CONNECTION = 'connection'; + private const CONFIG_CONNECTION = 'connection'; /** * Create a Connection @@ -152,7 +152,7 @@ private static function createStreamConnection($connection, AMQPConnectionConfig ); } - protected static function getReadWriteTimeout(AMQPConnectionConfig $config): float + private static function getReadWriteTimeout(AMQPConnectionConfig $config): float { return min($config->getReadTimeout(), $config->getWriteTimeout()); } diff --git a/src/Queue/QueueConfig.php b/src/Queue/QueueConfig.php index 6c4b5d49..7d3d6654 100644 --- a/src/Queue/QueueConfig.php +++ b/src/Queue/QueueConfig.php @@ -4,7 +4,7 @@ use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Jobs\RabbitMQJob; -class QueueConfig +final class QueueConfig { private string $queue = 'default'; @@ -109,7 +109,9 @@ public function getQueueMaxPriority(): int public function setQueueMaxPriority($queueMaxPriority): QueueConfig { - $this->queueMaxPriority = is_numeric($queueMaxPriority) ? (int) $queueMaxPriority : 2; + if (is_numeric($queueMaxPriority)) { + $this->queueMaxPriority = (int) $queueMaxPriority; + } return $this; } From 86181424f75fc6b03e75aa1c346ed28e908f26b5 Mon Sep 17 00:00:00 2001 From: Seb Date: Mon, 13 Mar 2023 13:33:50 -0700 Subject: [PATCH 270/375] Honor the 'rest' option --- src/Consumer.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Consumer.php b/src/Consumer.php index bd7f099d..a868d436 100644 --- a/src/Consumer.php +++ b/src/Consumer.php @@ -125,6 +125,10 @@ function (AMQPMessage $message) use ($connection, $options, $connectionName, $qu if ($this->supportsAsyncSignals()) { $this->resetTimeoutHandler(); } + + if ($options->rest > 0) { + sleep($options->rest); + } }, null, $arguments From e54d287dca2f70475adc97e163281a83be855d5b Mon Sep 17 00:00:00 2001 From: Seb Date: Mon, 13 Mar 2023 14:52:44 -0700 Subject: [PATCH 271/375] missed this --- src/Consumer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Consumer.php b/src/Consumer.php index a868d436..ed3d8099 100644 --- a/src/Consumer.php +++ b/src/Consumer.php @@ -127,7 +127,7 @@ function (AMQPMessage $message) use ($connection, $options, $connectionName, $qu } if ($options->rest > 0) { - sleep($options->rest); + $this->sleep($options->rest); } }, null, From 0e9c529550ed3cb2efe6f40bc77c8db41f30cff4 Mon Sep 17 00:00:00 2001 From: Emiel Bom Date: Wed, 15 Mar 2023 14:52:33 +0100 Subject: [PATCH 272/375] added: OctaneRabbitMQQueue::class --- src/Octane/RabbitMQQueue.php | 30 ++++++++++++++++++++++++++++++ src/Queue/QueueFactory.php | 5 +++++ 2 files changed, 35 insertions(+) create mode 100644 src/Octane/RabbitMQQueue.php diff --git a/src/Octane/RabbitMQQueue.php b/src/Octane/RabbitMQQueue.php new file mode 100644 index 00000000..e6bb8cb2 --- /dev/null +++ b/src/Octane/RabbitMQQueue.php @@ -0,0 +1,30 @@ +reconnect(); + parent::publishBasic($msg, $exchange, $destination, $mandatory, $immediate, $ticket); + } + } + + protected function publishBatch(): void + { + try { + parent::publishBatch(); + } catch (AMQPConnectionClosedException|AMQPChannelClosedException $exception) { + $this->reconnect(); + parent::publishBatch(); + } + } +} diff --git a/src/Queue/QueueFactory.php b/src/Queue/QueueFactory.php index 75bde1ed..ee29752b 100644 --- a/src/Queue/QueueFactory.php +++ b/src/Queue/QueueFactory.php @@ -4,6 +4,7 @@ use Illuminate\Support\Arr; use VladimirYuldashev\LaravelQueueRabbitMQ\Horizon\RabbitMQQueue as HorizonRabbitMQQueue; +use VladimirYuldashev\LaravelQueueRabbitMQ\Octane\RabbitMQQueue as OctaneRabbitMQQueue; class QueueFactory { @@ -20,6 +21,10 @@ public static function make(array $config = []): RabbitMQQueue return new HorizonRabbitMQQueue($queueConfig); } + if (strtolower($worker) == 'octane') { + return new OctaneRabbitMQQueue($queueConfig); + } + return new $worker($queueConfig); } } From 7d1ef065b4c87e4f4aaff3465fa157cd42575369 Mon Sep 17 00:00:00 2001 From: Emiel Bom Date: Wed, 15 Mar 2023 14:59:54 +0100 Subject: [PATCH 273/375] cleanup: - Connection disconnected by default (Lazy) - refactored publish methods --- src/Queue/RabbitMQQueue.php | 43 ++++++++++++++++++++++++++----------- 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index 22cced8b..8c67cc6d 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -19,6 +19,7 @@ use PhpAmqpLib\Exchange\AMQPExchangeType; use PhpAmqpLib\Message\AMQPMessage; use PhpAmqpLib\Wire\AMQPTable; +use RuntimeException; use Throwable; use VladimirYuldashev\LaravelQueueRabbitMQ\Contracts\RabbitMQQueueContract; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Jobs\RabbitMQJob; @@ -28,12 +29,12 @@ class RabbitMQQueue extends Queue implements QueueContract, RabbitMQQueueContrac /** * The RabbitMQ connection instance. */ - protected AbstractConnection $connection; + protected ?AbstractConnection $connection = null; /** * The RabbitMQ channel instance. */ - protected AMQPChannel $channel; + protected ?AMQPChannel $channel = null; /** * List of already declared exchanges. @@ -121,7 +122,7 @@ public function pushRaw($payload, $queue = null, array $options = []): int|strin [$message, $correlationId] = $this->createMessage($payload, $attempts); - $this->getChannel()->basic_publish($message, $exchange, $destination, true); + $this->publishBasic($message, $exchange, $destination, true); return $correlationId; } @@ -145,8 +146,6 @@ function ($payload, $queue, $delay) { } /** - * @param null $queue - * * @throws AMQPProtocolChannelException */ public function laterRaw($delay, string $payload, $queue = null, int $attempts = 0): int|string|null @@ -172,7 +171,7 @@ public function laterRaw($delay, string $payload, $queue = null, int $attempts = [$message, $correlationId] = $this->createMessage($payload, $attempts); // Publish directly on the delayQueue, no need to publish through an exchange. - $this->getChannel()->basic_publish($message, null, $destination, true); + $this->publishBasic($message, null, $destination, true); return $correlationId; } @@ -188,7 +187,7 @@ public function bulk($jobs, $data = '', $queue = null): void $this->bulkRaw($this->createPayload($job, $queue, $data), $queue, ['job' => $job]); } - $this->getChannel()->publish_batch(); + $this->publishBatch(); } /** @@ -235,7 +234,7 @@ public function pop($queue = null) if ($exception->amqp_reply_code === 404) { // Because of the channel exception the channel was closed and removed. // We have to open a new channel. Because else the worker(s) are stuck in a loop, without processing. - $this->channel = $this->getConnection()->channel(); + $this->getChannel(true); return null; } @@ -256,21 +255,31 @@ public function pop($queue = null) return null; } + /** + * @throws RuntimeException + */ public function getConnection(): AbstractConnection { + if (! $this->connection) { + throw new RuntimeException('Queue has no AMQPConnection set.'); + } + return $this->connection; } public function setConnection(AbstractConnection $connection): RabbitMQQueue { $this->connection = $connection; - $this->channel = $connection->channel(); return $this; } - public function getChannel(): AMQPChannel + public function getChannel($forceNew = false): AMQPChannel { + if (! $this->channel || $forceNew) { + $this->channel = $this->getConnection()->channel(); + } + return $this->channel; } @@ -563,7 +572,7 @@ public function close(): void } try { - $this->connection->close(); + $this->getConnection()->close(); } catch (ErrorException) { // Ignore the exception } @@ -709,8 +718,18 @@ protected function publishProperties($queue, array $options = []): array return [$destination, $exchange, $exchangeType, $attempts]; } - private function getConfig(): QueueConfig + protected function getConfig(): QueueConfig { return $this->config; } + + protected function publishBasic($msg, $exchange = '', $destination = '', $mandatory = false, $immediate = false, $ticket = null): void + { + $this->getChannel()->basic_publish($msg, $exchange, $destination, $mandatory, $immediate, $ticket); + } + + protected function publishBatch(): void + { + $this->getChannel()->publish_batch(); + } } From e25d51bad78a0401da4cf42d1366a32356127119 Mon Sep 17 00:00:00 2001 From: Emiel Bom Date: Wed, 15 Mar 2023 15:00:38 +0100 Subject: [PATCH 274/375] test lazy: - Connection disconnected by default (Lazy) --- tests/Feature/ConnectorTest.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/Feature/ConnectorTest.php b/tests/Feature/ConnectorTest.php index 00cf5d6e..3ecede98 100644 --- a/tests/Feature/ConnectorTest.php +++ b/tests/Feature/ConnectorTest.php @@ -48,8 +48,9 @@ public function testLazyConnection(): void $this->assertInstanceOf(RabbitMQQueue::class, $connection); $this->assertInstanceOf(AMQPLazyConnection::class, $connection->getConnection()); - $this->assertTrue($connection->getConnection()->isConnected()); + $this->assertFalse($connection->getConnection()->isConnected()); $this->assertTrue($connection->getChannel()->is_open()); + $this->assertTrue($connection->getConnection()->isConnected()); } public function testLazyStreamConnection(): void @@ -90,8 +91,9 @@ public function testLazyStreamConnection(): void $this->assertInstanceOf(RabbitMQQueue::class, $connection); $this->assertInstanceOf(AMQPStreamConnection::class, $connection->getConnection()); - $this->assertTrue($connection->getConnection()->isConnected()); + $this->assertFalse($connection->getConnection()->isConnected()); $this->assertTrue($connection->getChannel()->is_open()); + $this->assertTrue($connection->getConnection()->isConnected()); } public function testSslConnection(): void From 1a17d4c24447660f6f729740c1315db5ba21999f Mon Sep 17 00:00:00 2001 From: Emiel Bom Date: Wed, 15 Mar 2023 15:08:21 +0100 Subject: [PATCH 275/375] added: reconnect() method --- src/Queue/RabbitMQQueue.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index 8c67cc6d..a228d714 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -283,6 +283,12 @@ public function getChannel($forceNew = false): AMQPChannel return $this->channel; } + protected function reconnect() + { + $this->getConnection()->reconnect(); + $this->getChannel(true); + } + /** * Job class to use. * From 722bc8361c28adc99ae185f14f0224e66e6876c8 Mon Sep 17 00:00:00 2001 From: Emiel Bom Date: Wed, 15 Mar 2023 15:09:38 +0100 Subject: [PATCH 276/375] cleanup --- src/Octane/RabbitMQQueue.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Octane/RabbitMQQueue.php b/src/Octane/RabbitMQQueue.php index e6bb8cb2..d9cd9890 100644 --- a/src/Octane/RabbitMQQueue.php +++ b/src/Octane/RabbitMQQueue.php @@ -12,7 +12,7 @@ protected function publishBasic($msg, $exchange = '', $destination = '', $mandat { try { parent::publishBasic($msg, $exchange, $destination, $mandatory, $immediate, $ticket); - } catch (AMQPConnectionClosedException|AMQPChannelClosedException $exception) { + } catch (AMQPConnectionClosedException|AMQPChannelClosedException) { $this->reconnect(); parent::publishBasic($msg, $exchange, $destination, $mandatory, $immediate, $ticket); } @@ -22,7 +22,7 @@ protected function publishBatch(): void { try { parent::publishBatch(); - } catch (AMQPConnectionClosedException|AMQPChannelClosedException $exception) { + } catch (AMQPConnectionClosedException|AMQPChannelClosedException) { $this->reconnect(); parent::publishBatch(); } From 5edfec546eff1ccf10d006e793287fbcc818fde6 Mon Sep 17 00:00:00 2001 From: Emiel Bom Date: Wed, 15 Mar 2023 15:16:51 +0100 Subject: [PATCH 277/375] moved (reconnect) methods to a trait --- src/Octane/RabbitMQQueue.php | 23 ++--------------------- src/Queue/ReconnectTrait.php | 29 +++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 21 deletions(-) create mode 100644 src/Queue/ReconnectTrait.php diff --git a/src/Octane/RabbitMQQueue.php b/src/Octane/RabbitMQQueue.php index d9cd9890..9e749efa 100644 --- a/src/Octane/RabbitMQQueue.php +++ b/src/Octane/RabbitMQQueue.php @@ -2,29 +2,10 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Octane; -use PhpAmqpLib\Exception\AMQPChannelClosedException; -use PhpAmqpLib\Exception\AMQPConnectionClosedException; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue as BaseRabbitMQQueue; +use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\ReconnectTrait; class RabbitMQQueue extends BaseRabbitMQQueue { - protected function publishBasic($msg, $exchange = '', $destination = '', $mandatory = false, $immediate = false, $ticket = null): void - { - try { - parent::publishBasic($msg, $exchange, $destination, $mandatory, $immediate, $ticket); - } catch (AMQPConnectionClosedException|AMQPChannelClosedException) { - $this->reconnect(); - parent::publishBasic($msg, $exchange, $destination, $mandatory, $immediate, $ticket); - } - } - - protected function publishBatch(): void - { - try { - parent::publishBatch(); - } catch (AMQPConnectionClosedException|AMQPChannelClosedException) { - $this->reconnect(); - parent::publishBatch(); - } - } + use ReconnectTrait; } diff --git a/src/Queue/ReconnectTrait.php b/src/Queue/ReconnectTrait.php new file mode 100644 index 00000000..41ec7395 --- /dev/null +++ b/src/Queue/ReconnectTrait.php @@ -0,0 +1,29 @@ +reconnect(); + parent::publishBasic($msg, $exchange, $destination, $mandatory, $immediate, $ticket); + } + } + + protected function publishBatch(): void + { + try { + parent::publishBatch(); + } catch (AMQPConnectionClosedException|AMQPChannelClosedException) { + $this->reconnect(); + parent::publishBatch(); + } + } +} From 0b6699161465e09708b4d3564107206de7e2b865 Mon Sep 17 00:00:00 2001 From: Emiel Bom Date: Wed, 15 Mar 2023 18:08:43 +0100 Subject: [PATCH 278/375] added tests --- tests/Functional/RabbitMQQueueTest.php | 67 ++++++++++++++++++++------ tests/Functional/TestCase.php | 22 ++++++++- 2 files changed, 72 insertions(+), 17 deletions(-) diff --git a/tests/Functional/RabbitMQQueueTest.php b/tests/Functional/RabbitMQQueueTest.php index 5a50342b..69f8c4b4 100644 --- a/tests/Functional/RabbitMQQueueTest.php +++ b/tests/Functional/RabbitMQQueueTest.php @@ -2,10 +2,14 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Functional; +use Illuminate\Support\Facades\Queue; use Illuminate\Support\Str; +use PhpAmqpLib\Exception\AMQPChannelClosedException; use PhpAmqpLib\Exchange\AMQPExchangeType; +use VladimirYuldashev\LaravelQueueRabbitMQ\Octane\RabbitMQQueue as OctaneRabbitMQQueue; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue; use VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Functional\TestCase as BaseTestCase; +use VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Mocks\TestJob; class RabbitMQQueueTest extends BaseTestCase { @@ -19,6 +23,10 @@ public function testConnection(): void $queue = $this->connection('rabbitmq-with-options-empty'); $this->assertInstanceOf(RabbitMQQueue::class, $queue); + + $queue = $this->connection('rabbitmq-with-octane-reconnect-options'); + $this->assertInstanceOf(RabbitMQQueue::class, $queue); + $this->assertInstanceOf(OctaneRabbitMQQueue::class, $queue); } public function testConfigRerouteFailed(): void @@ -180,14 +188,14 @@ public function testQueueArguments(): void { $name = Str::random(); - $queue = $this->connection(); - $actual = $this->callMethod($queue, 'getQueueArguments', [$name]); + $queue = $this->connection(); + $actual = $this->callMethod($queue, 'getQueueArguments', [$name]); $expected = []; $this->assertEquals(array_keys($expected), array_keys($actual)); $this->assertEquals(array_values($expected), array_values($actual)); - $queue = $this->connection('rabbitmq-with-options'); - $actual = $this->callMethod($queue, 'getQueueArguments', [$name]); + $queue = $this->connection('rabbitmq-with-options'); + $actual = $this->callMethod($queue, 'getQueueArguments', [$name]); $expected = [ 'x-max-priority' => 20, 'x-dead-letter-exchange' => 'failed-exchange', @@ -197,8 +205,8 @@ public function testQueueArguments(): void $this->assertEquals(array_keys($expected), array_keys($actual)); $this->assertEquals(array_values($expected), array_values($actual)); - $queue = $this->connection('rabbitmq-with-quorum-options'); - $actual = $this->callMethod($queue, 'getQueueArguments', [$name]); + $queue = $this->connection('rabbitmq-with-quorum-options'); + $actual = $this->callMethod($queue, 'getQueueArguments', [$name]); $expected = [ 'x-dead-letter-exchange' => 'failed-exchange', 'x-dead-letter-routing-key' => sprintf('application-x.%s.failed', $name), @@ -208,8 +216,8 @@ public function testQueueArguments(): void $this->assertEquals(array_keys($expected), array_keys($actual)); $this->assertEquals(array_values($expected), array_values($actual)); - $queue = $this->connection('rabbitmq-with-options-empty'); - $actual = $this->callMethod($queue, 'getQueueArguments', [$name]); + $queue = $this->connection('rabbitmq-with-options-empty'); + $actual = $this->callMethod($queue, 'getQueueArguments', [$name]); $expected = []; $this->assertEquals(array_keys($expected), array_keys($actual)); @@ -219,10 +227,10 @@ public function testQueueArguments(): void public function testDelayQueueArguments(): void { $name = Str::random(); - $ttl = 12000; + $ttl = 12000; - $queue = $this->connection(); - $actual = $this->callMethod($queue, 'getDelayQueueArguments', [$name, $ttl]); + $queue = $this->connection(); + $actual = $this->callMethod($queue, 'getDelayQueueArguments', [$name, $ttl]); $expected = [ 'x-dead-letter-exchange' => '', 'x-dead-letter-routing-key' => $name, @@ -232,8 +240,8 @@ public function testDelayQueueArguments(): void $this->assertEquals(array_keys($expected), array_keys($actual)); $this->assertEquals(array_values($expected), array_values($actual)); - $queue = $this->connection('rabbitmq-with-options'); - $actual = $this->callMethod($queue, 'getDelayQueueArguments', [$name, $ttl]); + $queue = $this->connection('rabbitmq-with-options'); + $actual = $this->callMethod($queue, 'getDelayQueueArguments', [$name, $ttl]); $expected = [ 'x-dead-letter-exchange' => 'application-x', 'x-dead-letter-routing-key' => sprintf('process.%s', $name), @@ -243,8 +251,8 @@ public function testDelayQueueArguments(): void $this->assertEquals(array_keys($expected), array_keys($actual)); $this->assertEquals(array_values($expected), array_values($actual)); - $queue = $this->connection('rabbitmq-with-options-empty'); - $actual = $this->callMethod($queue, 'getDelayQueueArguments', [$name, $ttl]); + $queue = $this->connection('rabbitmq-with-options-empty'); + $actual = $this->callMethod($queue, 'getDelayQueueArguments', [$name, $ttl]); $expected = [ 'x-dead-letter-exchange' => '', 'x-dead-letter-routing-key' => $name, @@ -254,4 +262,33 @@ public function testDelayQueueArguments(): void $this->assertEquals(array_keys($expected), array_keys($actual)); $this->assertEquals(array_values($expected), array_values($actual)); } + + public function testWithoutReconnect(): void + { + $queue = $this->connection(); + $queue->purge(); + // Lazy connection + $queue->push(new TestJob()); + sleep(1); + $this->assertSame(1, $queue->size()); + + $queue->getConnection()->close(); + $this->assertFalse($queue->getConnection()->isConnected()); + $this->assertThrows(fn() => $queue->push(new TestJob()), AMQPChannelClosedException::class); + } + + public function testReconnect(): void + { + $queue = $this->connection('rabbitmq-with-octane-reconnect-options'); + $queue->purge(); + $queue->push(new TestJob()); + sleep(1); + $this->assertSame(1, $queue->size()); + $queue->getConnection()->close(); + $this->assertFalse($queue->getConnection()->isConnected()); + $queue->push(new TestJob()); + sleep(1); + $this->assertTrue($queue->getConnection()->isConnected()); + $this->assertSame(2, $queue->size()); + } } diff --git a/tests/Functional/TestCase.php b/tests/Functional/TestCase.php index 4edc8853..384e0cef 100644 --- a/tests/Functional/TestCase.php +++ b/tests/Functional/TestCase.php @@ -157,6 +157,24 @@ protected function getEnvironmentSetUp($app): void 'worker' => 'default', ]); + $app['config']->set('queue.connections.rabbitmq-with-octane-reconnect-options', [ + 'driver' => 'rabbitmq', + 'queue' => 'default', + 'connection' => 'default', + + 'hosts' => [ + [ + 'host' => getenv('HOST'), + 'port' => getenv('PORT'), + 'vhost' => '/', + 'user' => 'guest', + 'password' => 'guest', + ], + ], + + 'worker' => 'octane', + + ]); } /** @@ -165,7 +183,7 @@ protected function getEnvironmentSetUp($app): void protected function callMethod($object, string $method, array $parameters = []): mixed { try { - $className = get_class($object); + $className = get_class($object); $reflection = new ReflectionClass($className); } catch (ReflectionException $e) { throw new Exception($e->getMessage()); @@ -183,7 +201,7 @@ protected function callMethod($object, string $method, array $parameters = []): protected function callProperty($object, string $property): mixed { try { - $className = get_class($object); + $className = get_class($object); $reflection = new ReflectionClass($className); } catch (ReflectionException $e) { throw new Exception($e->getMessage()); From 326cb5171dc70304214bda888f6ac1a30e6051fe Mon Sep 17 00:00:00 2001 From: Emiel Bom Date: Wed, 15 Mar 2023 18:08:56 +0100 Subject: [PATCH 279/375] added tests --- tests/Functional/RabbitMQQueueTest.php | 33 +++++++++++++------------- tests/Functional/TestCase.php | 4 ++-- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/tests/Functional/RabbitMQQueueTest.php b/tests/Functional/RabbitMQQueueTest.php index 69f8c4b4..9bad51f5 100644 --- a/tests/Functional/RabbitMQQueueTest.php +++ b/tests/Functional/RabbitMQQueueTest.php @@ -2,7 +2,6 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Functional; -use Illuminate\Support\Facades\Queue; use Illuminate\Support\Str; use PhpAmqpLib\Exception\AMQPChannelClosedException; use PhpAmqpLib\Exchange\AMQPExchangeType; @@ -188,14 +187,14 @@ public function testQueueArguments(): void { $name = Str::random(); - $queue = $this->connection(); - $actual = $this->callMethod($queue, 'getQueueArguments', [$name]); + $queue = $this->connection(); + $actual = $this->callMethod($queue, 'getQueueArguments', [$name]); $expected = []; $this->assertEquals(array_keys($expected), array_keys($actual)); $this->assertEquals(array_values($expected), array_values($actual)); - $queue = $this->connection('rabbitmq-with-options'); - $actual = $this->callMethod($queue, 'getQueueArguments', [$name]); + $queue = $this->connection('rabbitmq-with-options'); + $actual = $this->callMethod($queue, 'getQueueArguments', [$name]); $expected = [ 'x-max-priority' => 20, 'x-dead-letter-exchange' => 'failed-exchange', @@ -205,8 +204,8 @@ public function testQueueArguments(): void $this->assertEquals(array_keys($expected), array_keys($actual)); $this->assertEquals(array_values($expected), array_values($actual)); - $queue = $this->connection('rabbitmq-with-quorum-options'); - $actual = $this->callMethod($queue, 'getQueueArguments', [$name]); + $queue = $this->connection('rabbitmq-with-quorum-options'); + $actual = $this->callMethod($queue, 'getQueueArguments', [$name]); $expected = [ 'x-dead-letter-exchange' => 'failed-exchange', 'x-dead-letter-routing-key' => sprintf('application-x.%s.failed', $name), @@ -216,8 +215,8 @@ public function testQueueArguments(): void $this->assertEquals(array_keys($expected), array_keys($actual)); $this->assertEquals(array_values($expected), array_values($actual)); - $queue = $this->connection('rabbitmq-with-options-empty'); - $actual = $this->callMethod($queue, 'getQueueArguments', [$name]); + $queue = $this->connection('rabbitmq-with-options-empty'); + $actual = $this->callMethod($queue, 'getQueueArguments', [$name]); $expected = []; $this->assertEquals(array_keys($expected), array_keys($actual)); @@ -227,10 +226,10 @@ public function testQueueArguments(): void public function testDelayQueueArguments(): void { $name = Str::random(); - $ttl = 12000; + $ttl = 12000; - $queue = $this->connection(); - $actual = $this->callMethod($queue, 'getDelayQueueArguments', [$name, $ttl]); + $queue = $this->connection(); + $actual = $this->callMethod($queue, 'getDelayQueueArguments', [$name, $ttl]); $expected = [ 'x-dead-letter-exchange' => '', 'x-dead-letter-routing-key' => $name, @@ -240,8 +239,8 @@ public function testDelayQueueArguments(): void $this->assertEquals(array_keys($expected), array_keys($actual)); $this->assertEquals(array_values($expected), array_values($actual)); - $queue = $this->connection('rabbitmq-with-options'); - $actual = $this->callMethod($queue, 'getDelayQueueArguments', [$name, $ttl]); + $queue = $this->connection('rabbitmq-with-options'); + $actual = $this->callMethod($queue, 'getDelayQueueArguments', [$name, $ttl]); $expected = [ 'x-dead-letter-exchange' => 'application-x', 'x-dead-letter-routing-key' => sprintf('process.%s', $name), @@ -251,8 +250,8 @@ public function testDelayQueueArguments(): void $this->assertEquals(array_keys($expected), array_keys($actual)); $this->assertEquals(array_values($expected), array_values($actual)); - $queue = $this->connection('rabbitmq-with-options-empty'); - $actual = $this->callMethod($queue, 'getDelayQueueArguments', [$name, $ttl]); + $queue = $this->connection('rabbitmq-with-options-empty'); + $actual = $this->callMethod($queue, 'getDelayQueueArguments', [$name, $ttl]); $expected = [ 'x-dead-letter-exchange' => '', 'x-dead-letter-routing-key' => $name, @@ -274,7 +273,7 @@ public function testWithoutReconnect(): void $queue->getConnection()->close(); $this->assertFalse($queue->getConnection()->isConnected()); - $this->assertThrows(fn() => $queue->push(new TestJob()), AMQPChannelClosedException::class); + $this->assertThrows(fn () => $queue->push(new TestJob()), AMQPChannelClosedException::class); } public function testReconnect(): void diff --git a/tests/Functional/TestCase.php b/tests/Functional/TestCase.php index 384e0cef..b57fd768 100644 --- a/tests/Functional/TestCase.php +++ b/tests/Functional/TestCase.php @@ -183,7 +183,7 @@ protected function getEnvironmentSetUp($app): void protected function callMethod($object, string $method, array $parameters = []): mixed { try { - $className = get_class($object); + $className = get_class($object); $reflection = new ReflectionClass($className); } catch (ReflectionException $e) { throw new Exception($e->getMessage()); @@ -201,7 +201,7 @@ protected function callMethod($object, string $method, array $parameters = []): protected function callProperty($object, string $property): mixed { try { - $className = get_class($object); + $className = get_class($object); $reflection = new ReflectionClass($className); } catch (ReflectionException $e) { throw new Exception($e->getMessage()); From c3d53dbd234c919a20a909e37095f24ba1575d3c Mon Sep 17 00:00:00 2001 From: Emiel Bom Date: Thu, 16 Mar 2023 10:58:58 +0100 Subject: [PATCH 280/375] added tests --- tests/Feature/QueueTest.php | 44 ++++++++++++++++++++++++++ tests/Functional/RabbitMQQueueTest.php | 31 ------------------ tests/TestCase.php | 20 ++++++++++++ 3 files changed, 64 insertions(+), 31 deletions(-) diff --git a/tests/Feature/QueueTest.php b/tests/Feature/QueueTest.php index 3258b7f2..357d9b01 100644 --- a/tests/Feature/QueueTest.php +++ b/tests/Feature/QueueTest.php @@ -3,11 +3,55 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Feature; use PhpAmqpLib\Connection\AMQPStreamConnection; +use PhpAmqpLib\Exception\AMQPChannelClosedException; +use PhpAmqpLib\Exception\AMQPConnectionClosedException; +use PhpAmqpLib\Exception\AMQPProtocolChannelException; +use VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Mocks\TestJob; class QueueTest extends TestCase { + public function setUp(): void + { + parent::setUp(); + + $this->withoutExceptionHandling([AMQPChannelClosedException::class, AMQPConnectionClosedException::class, AMQPProtocolChannelException::class]); + } + public function testConnection(): void { $this->assertInstanceOf(AMQPStreamConnection::class, $this->connection()->getChannel()->getConnection()); } + + public function testWithoutReconnect(): void + { + $queue = $this->connection('rabbitmq'); + + $queue->push(new TestJob()); + sleep(1); + $this->assertSame(1, $queue->size()); + + // close connection + $queue->getConnection()->close(); + $this->assertFalse($queue->getConnection()->isConnected()); + + $this->assertThrows(fn () => $queue->push(new TestJob()), AMQPChannelClosedException::class); + } + + public function testReconnect(): void + { + $queue = $this->connection('octane'); + + $queue->push(new TestJob()); + sleep(1); + $this->assertSame(1, $queue->size()); + + // close connection + $queue->getConnection()->close(); + $this->assertFalse($queue->getConnection()->isConnected()); + + $queue->push(new TestJob()); + sleep(1); + $this->assertTrue($queue->getConnection()->isConnected()); + $this->assertSame(2, $queue->size()); + } } diff --git a/tests/Functional/RabbitMQQueueTest.php b/tests/Functional/RabbitMQQueueTest.php index 9bad51f5..fac5e889 100644 --- a/tests/Functional/RabbitMQQueueTest.php +++ b/tests/Functional/RabbitMQQueueTest.php @@ -3,12 +3,10 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Functional; use Illuminate\Support\Str; -use PhpAmqpLib\Exception\AMQPChannelClosedException; use PhpAmqpLib\Exchange\AMQPExchangeType; use VladimirYuldashev\LaravelQueueRabbitMQ\Octane\RabbitMQQueue as OctaneRabbitMQQueue; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue; use VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Functional\TestCase as BaseTestCase; -use VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Mocks\TestJob; class RabbitMQQueueTest extends BaseTestCase { @@ -261,33 +259,4 @@ public function testDelayQueueArguments(): void $this->assertEquals(array_keys($expected), array_keys($actual)); $this->assertEquals(array_values($expected), array_values($actual)); } - - public function testWithoutReconnect(): void - { - $queue = $this->connection(); - $queue->purge(); - // Lazy connection - $queue->push(new TestJob()); - sleep(1); - $this->assertSame(1, $queue->size()); - - $queue->getConnection()->close(); - $this->assertFalse($queue->getConnection()->isConnected()); - $this->assertThrows(fn () => $queue->push(new TestJob()), AMQPChannelClosedException::class); - } - - public function testReconnect(): void - { - $queue = $this->connection('rabbitmq-with-octane-reconnect-options'); - $queue->purge(); - $queue->push(new TestJob()); - sleep(1); - $this->assertSame(1, $queue->size()); - $queue->getConnection()->close(); - $this->assertFalse($queue->getConnection()->isConnected()); - $queue->push(new TestJob()); - sleep(1); - $this->assertTrue($queue->getConnection()->isConnected()); - $this->assertSame(2, $queue->size()); - } } diff --git a/tests/TestCase.php b/tests/TestCase.php index 7d50fa67..9bea59b3 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -48,6 +48,26 @@ protected function getEnvironmentSetUp($app): void 'worker' => 'default', ]); + $app['config']->set('queue.connections.octane', [ + 'driver' => 'rabbitmq', + 'queue' => 'default', + 'connection' => AMQPLazyConnection::class, + + 'hosts' => [ + [ + 'host' => getenv('HOST'), + 'port' => getenv('PORT'), + 'vhost' => '/', + 'user' => 'guest', + 'password' => 'guest', + ], + ], + + 'options' => [], + + 'worker' => 'octane', + + ]); } protected function connection(string $name = null): RabbitMQQueue From e53fea0ecc90574f8bd5f7b6c8cd18a1978771ba Mon Sep 17 00:00:00 2001 From: Emiel Bom Date: Thu, 16 Mar 2023 11:20:44 +0100 Subject: [PATCH 281/375] updated: - removed `final` - changed `private` to `protected` --- src/Queue/Connection/ConfigFactory.php | 10 +++--- src/Queue/Connection/ConnectionFactory.php | 36 +++++++++++----------- src/Queue/Connectors/RabbitMQConnector.php | 2 +- src/Queue/QueueConfig.php | 28 ++++++++--------- src/Queue/QueueConfigFactory.php | 4 +-- 5 files changed, 40 insertions(+), 40 deletions(-) diff --git a/src/Queue/Connection/ConfigFactory.php b/src/Queue/Connection/ConfigFactory.php index 9e27529d..4b8f87a1 100644 --- a/src/Queue/Connection/ConfigFactory.php +++ b/src/Queue/Connection/ConfigFactory.php @@ -5,11 +5,11 @@ use Illuminate\Support\Arr; use PhpAmqpLib\Connection\AMQPConnectionConfig; -final class ConfigFactory +class ConfigFactory { public const CONFIG_HOSTS = 'hosts'; - private const CONFIG_OPTIONS = 'options'; + protected const CONFIG_OPTIONS = 'options'; /** * Create a config object from config array @@ -40,7 +40,7 @@ public static function make(array $config = []): AMQPConnectionConfig }); } - private static function getHostFromConfig(AMQPConnectionConfig $connectionConfig, array $config): void + protected static function getHostFromConfig(AMQPConnectionConfig $connectionConfig, array $config): void { $hostConfig = Arr::first(Arr::shuffle(Arr::get($config, self::CONFIG_HOSTS, [])), null, []); @@ -61,7 +61,7 @@ private static function getHostFromConfig(AMQPConnectionConfig $connectionConfig } } - private static function getSLLOptionsFromConfig(AMQPConnectionConfig $connectionConfig, array $config): void + protected static function getSLLOptionsFromConfig(AMQPConnectionConfig $connectionConfig, array $config): void { $sslConfig = Arr::get($config, self::CONFIG_OPTIONS.'.ssl_options', []); @@ -82,7 +82,7 @@ private static function getSLLOptionsFromConfig(AMQPConnectionConfig $connection } } - private static function getHeartbeatFromConfig(AMQPConnectionConfig $connectionConfig, array $config): void + protected static function getHeartbeatFromConfig(AMQPConnectionConfig $connectionConfig, array $config): void { $heartbeat = Arr::get($config, self::CONFIG_OPTIONS.'.heartbeat'); diff --git a/src/Queue/Connection/ConnectionFactory.php b/src/Queue/Connection/ConnectionFactory.php index 2ad99176..df19f223 100644 --- a/src/Queue/Connection/ConnectionFactory.php +++ b/src/Queue/Connection/ConnectionFactory.php @@ -17,17 +17,17 @@ class ConnectionFactory { - private const CONNECTION_TYPE_DEFAULT = 'default'; + protected const CONNECTION_TYPE_DEFAULT = 'default'; - private const CONNECTION_TYPE_EXTENDED = AbstractConnection::class; + protected const CONNECTION_TYPE_EXTENDED = AbstractConnection::class; - private const CONNECTION_SUB_TYPE_STREAM = AMQPStreamConnection::class; + protected const CONNECTION_SUB_TYPE_STREAM = AMQPStreamConnection::class; - private const CONNECTION_SUB_TYPE_SOCKET = AMQPSocketConnection::class; + protected const CONNECTION_SUB_TYPE_SOCKET = AMQPSocketConnection::class; - private const CONNECTION_SUB_TYPE_SSL = AMQPSSLConnection::class; + protected const CONNECTION_SUB_TYPE_SSL = AMQPSSLConnection::class; - private const CONFIG_CONNECTION = 'connection'; + protected const CONFIG_CONNECTION = 'connection'; /** * Create a Connection @@ -58,7 +58,7 @@ public static function make(array $config = []): AbstractConnection /** * Get the validated connection from config */ - private static function getConnectionFromConfig(array $config): string + protected static function getConnectionFromConfig(array $config): string { $connection = (string) Arr::get($config, self::CONFIG_CONNECTION, self::CONNECTION_TYPE_DEFAULT); @@ -70,7 +70,7 @@ private static function getConnectionFromConfig(array $config): string /** * Creation of your own connection */ - private static function create($connection, AMQPConnectionConfig $config): AbstractConnection + protected static function create($connection, AMQPConnectionConfig $config): AbstractConnection { if ($config->getIoType() === AMQPConnectionConfig::IO_TYPE_SOCKET) { return self::createSocketConnection($connection, $config); @@ -79,7 +79,7 @@ private static function create($connection, AMQPConnectionConfig $config): Abstr return self::createStreamConnection($connection, $config); } - private static function createSocketConnection($connection, AMQPConnectionConfig $config): AMQPSocketConnection + protected static function createSocketConnection($connection, AMQPConnectionConfig $config): AMQPSocketConnection { self::assertSocketConnection($connection, $config); @@ -102,7 +102,7 @@ private static function createSocketConnection($connection, AMQPConnectionConfig ); } - private static function createStreamConnection($connection, AMQPConnectionConfig $config): AMQPStreamConnection + protected static function createStreamConnection($connection, AMQPConnectionConfig $config): AMQPStreamConnection { self::assertStreamConnection($connection); @@ -152,12 +152,12 @@ private static function createStreamConnection($connection, AMQPConnectionConfig ); } - private static function getReadWriteTimeout(AMQPConnectionConfig $config): float + protected static function getReadWriteTimeout(AMQPConnectionConfig $config): float { return min($config->getReadTimeout(), $config->getWriteTimeout()); } - private static function getSslOptions(AMQPConnectionConfig $config): array + protected static function getSslOptions(AMQPConnectionConfig $config): array { return array_filter([ 'cafile' => $config->getSslCaCert(), @@ -174,14 +174,14 @@ private static function getSslOptions(AMQPConnectionConfig $config): array }); } - private static function assertConnectionFromConfig(string $connection): void + protected static function assertConnectionFromConfig(string $connection): void { if ($connection !== self::CONNECTION_TYPE_DEFAULT && ! is_subclass_of($connection, self::CONNECTION_TYPE_EXTENDED)) { throw new AMQPLogicException(sprintf('The config property \'%s\' must contain \'%s\' or must extend: %s', self::CONFIG_CONNECTION, self::CONNECTION_TYPE_DEFAULT, class_basename(self::CONNECTION_TYPE_EXTENDED))); } } - private static function assertSocketConnection($connection, AMQPConnectionConfig $config): void + protected static function assertSocketConnection($connection, AMQPConnectionConfig $config): void { self::assertExtendedOf($connection, self::CONNECTION_SUB_TYPE_SOCKET); @@ -190,17 +190,17 @@ private static function assertSocketConnection($connection, AMQPConnectionConfig } } - private static function assertStreamConnection($connection): void + protected static function assertStreamConnection($connection): void { self::assertExtendedOf($connection, self::CONNECTION_SUB_TYPE_STREAM); } - private static function assertSSLConnection($connection): void + protected static function assertSSLConnection($connection): void { self::assertExtendedOf($connection, self::CONNECTION_SUB_TYPE_SSL); } - private static function assertExtendedOf($connection, string $abstract): void + protected static function assertExtendedOf($connection, string $abstract): void { if (! is_subclass_of($connection, $abstract)) { throw new AMQPLogicException(sprintf('The connection must extend: %s', class_basename($abstract))); @@ -214,7 +214,7 @@ private static function assertExtendedOf($connection, string $abstract): void * * @deprecated This is the fallback method, update your config asap. (example: connection => 'default') */ - private static function _createLazyConnection($connection, array $config): AbstractConnection + protected static function _createLazyConnection($connection, array $config): AbstractConnection { return $connection::create_connection( Arr::shuffle(Arr::get($config, ConfigFactory::CONFIG_HOSTS, [])), diff --git a/src/Queue/Connectors/RabbitMQConnector.php b/src/Queue/Connectors/RabbitMQConnector.php index 97c964c1..a0f79e32 100644 --- a/src/Queue/Connectors/RabbitMQConnector.php +++ b/src/Queue/Connectors/RabbitMQConnector.php @@ -16,7 +16,7 @@ class RabbitMQConnector implements ConnectorInterface { - private Dispatcher $dispatcher; + protected Dispatcher $dispatcher; public function __construct(Dispatcher $dispatcher) { diff --git a/src/Queue/QueueConfig.php b/src/Queue/QueueConfig.php index 7d3d6654..b5aea131 100644 --- a/src/Queue/QueueConfig.php +++ b/src/Queue/QueueConfig.php @@ -4,33 +4,33 @@ use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Jobs\RabbitMQJob; -final class QueueConfig +class QueueConfig { - private string $queue = 'default'; + protected string $queue = 'default'; - private bool $dispatchAfterCommit = false; + protected bool $dispatchAfterCommit = false; - private string $abstractJob = RabbitMQJob::class; + protected string $abstractJob = RabbitMQJob::class; - private bool $prioritizeDelayed = false; + protected bool $prioritizeDelayed = false; - private int $queueMaxPriority = 2; + protected int $queueMaxPriority = 2; - private ?string $exchange = null; + protected ?string $exchange = null; - private string $exchangeType = 'direct'; + protected string $exchangeType = 'direct'; - private string $exchangeRoutingKey = '%s'; + protected string $exchangeRoutingKey = '%s'; - private bool $rerouteFailed = false; + protected bool $rerouteFailed = false; - private ?string $failedExchange = null; + protected ?string $failedExchange = null; - private string $failedRoutingKey = '%s.failed'; + protected string $failedRoutingKey = '%s.failed'; - private bool $quorum = false; + protected bool $quorum = false; - private array $options = []; + protected array $options = []; public function getQueue(): string { diff --git a/src/Queue/QueueConfigFactory.php b/src/Queue/QueueConfigFactory.php index 6266cf20..87fc2fac 100644 --- a/src/Queue/QueueConfigFactory.php +++ b/src/Queue/QueueConfigFactory.php @@ -6,7 +6,7 @@ class QueueConfigFactory { - private const CONFIG_OPTIONS = 'options'; + protected const CONFIG_OPTIONS = 'options'; /** * Create a config object from config array @@ -25,7 +25,7 @@ public static function make(array $config = []): QueueConfig }); } - private static function getOptionsFromConfig(QueueConfig $queueConfig, array $config): void + protected static function getOptionsFromConfig(QueueConfig $queueConfig, array $config): void { $queueOptions = Arr::get($config, self::CONFIG_OPTIONS.'.queue', []) ?: []; From 947ba6aa86029b57a57b61ad9dc53e247ee3e82d Mon Sep 17 00:00:00 2001 From: Emiel Bom Date: Thu, 16 Mar 2023 11:54:05 +0100 Subject: [PATCH 282/375] throw test --- tests/Feature/QueueTest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/Feature/QueueTest.php b/tests/Feature/QueueTest.php index 357d9b01..0f8681fc 100644 --- a/tests/Feature/QueueTest.php +++ b/tests/Feature/QueueTest.php @@ -34,7 +34,8 @@ public function testWithoutReconnect(): void $queue->getConnection()->close(); $this->assertFalse($queue->getConnection()->isConnected()); - $this->assertThrows(fn () => $queue->push(new TestJob()), AMQPChannelClosedException::class); + $this->expectException(AMQPChannelClosedException::class); + $queue->push(new TestJob()); } public function testReconnect(): void From bdbdc42fcf0a17629525b8ebe512e3da4c1dd292 Mon Sep 17 00:00:00 2001 From: Emiel Bom Date: Wed, 15 Mar 2023 18:08:43 +0100 Subject: [PATCH 283/375] added tests --- tests/Feature/QueueTest.php | 45 ++++++++++++++++++++++++++ tests/Functional/RabbitMQQueueTest.php | 5 +++ tests/TestCase.php | 20 ++++++++++++ 3 files changed, 70 insertions(+) diff --git a/tests/Feature/QueueTest.php b/tests/Feature/QueueTest.php index 3258b7f2..0f8681fc 100644 --- a/tests/Feature/QueueTest.php +++ b/tests/Feature/QueueTest.php @@ -3,11 +3,56 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Feature; use PhpAmqpLib\Connection\AMQPStreamConnection; +use PhpAmqpLib\Exception\AMQPChannelClosedException; +use PhpAmqpLib\Exception\AMQPConnectionClosedException; +use PhpAmqpLib\Exception\AMQPProtocolChannelException; +use VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Mocks\TestJob; class QueueTest extends TestCase { + public function setUp(): void + { + parent::setUp(); + + $this->withoutExceptionHandling([AMQPChannelClosedException::class, AMQPConnectionClosedException::class, AMQPProtocolChannelException::class]); + } + public function testConnection(): void { $this->assertInstanceOf(AMQPStreamConnection::class, $this->connection()->getChannel()->getConnection()); } + + public function testWithoutReconnect(): void + { + $queue = $this->connection('rabbitmq'); + + $queue->push(new TestJob()); + sleep(1); + $this->assertSame(1, $queue->size()); + + // close connection + $queue->getConnection()->close(); + $this->assertFalse($queue->getConnection()->isConnected()); + + $this->expectException(AMQPChannelClosedException::class); + $queue->push(new TestJob()); + } + + public function testReconnect(): void + { + $queue = $this->connection('octane'); + + $queue->push(new TestJob()); + sleep(1); + $this->assertSame(1, $queue->size()); + + // close connection + $queue->getConnection()->close(); + $this->assertFalse($queue->getConnection()->isConnected()); + + $queue->push(new TestJob()); + sleep(1); + $this->assertTrue($queue->getConnection()->isConnected()); + $this->assertSame(2, $queue->size()); + } } diff --git a/tests/Functional/RabbitMQQueueTest.php b/tests/Functional/RabbitMQQueueTest.php index 5a50342b..fac5e889 100644 --- a/tests/Functional/RabbitMQQueueTest.php +++ b/tests/Functional/RabbitMQQueueTest.php @@ -4,6 +4,7 @@ use Illuminate\Support\Str; use PhpAmqpLib\Exchange\AMQPExchangeType; +use VladimirYuldashev\LaravelQueueRabbitMQ\Octane\RabbitMQQueue as OctaneRabbitMQQueue; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue; use VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Functional\TestCase as BaseTestCase; @@ -19,6 +20,10 @@ public function testConnection(): void $queue = $this->connection('rabbitmq-with-options-empty'); $this->assertInstanceOf(RabbitMQQueue::class, $queue); + + $queue = $this->connection('rabbitmq-with-octane-reconnect-options'); + $this->assertInstanceOf(RabbitMQQueue::class, $queue); + $this->assertInstanceOf(OctaneRabbitMQQueue::class, $queue); } public function testConfigRerouteFailed(): void diff --git a/tests/TestCase.php b/tests/TestCase.php index 7d50fa67..9bea59b3 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -48,6 +48,26 @@ protected function getEnvironmentSetUp($app): void 'worker' => 'default', ]); + $app['config']->set('queue.connections.octane', [ + 'driver' => 'rabbitmq', + 'queue' => 'default', + 'connection' => AMQPLazyConnection::class, + + 'hosts' => [ + [ + 'host' => getenv('HOST'), + 'port' => getenv('PORT'), + 'vhost' => '/', + 'user' => 'guest', + 'password' => 'guest', + ], + ], + + 'options' => [], + + 'worker' => 'octane', + + ]); } protected function connection(string $name = null): RabbitMQQueue From 7897f77654ea0a5dcf1ec43239b88b00f948ba2c Mon Sep 17 00:00:00 2001 From: Emiel Bom Date: Thu, 16 Mar 2023 12:18:46 +0100 Subject: [PATCH 284/375] removed unnecessary connection config --- tests/Functional/TestCase.php | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/tests/Functional/TestCase.php b/tests/Functional/TestCase.php index b57fd768..4edc8853 100644 --- a/tests/Functional/TestCase.php +++ b/tests/Functional/TestCase.php @@ -157,24 +157,6 @@ protected function getEnvironmentSetUp($app): void 'worker' => 'default', ]); - $app['config']->set('queue.connections.rabbitmq-with-octane-reconnect-options', [ - 'driver' => 'rabbitmq', - 'queue' => 'default', - 'connection' => 'default', - - 'hosts' => [ - [ - 'host' => getenv('HOST'), - 'port' => getenv('PORT'), - 'vhost' => '/', - 'user' => 'guest', - 'password' => 'guest', - ], - ], - - 'worker' => 'octane', - - ]); } /** From 453c890a9052c5c3d3d8c4d4ba5c052632667beb Mon Sep 17 00:00:00 2001 From: Emiel Bom Date: Thu, 16 Mar 2023 13:20:43 +0100 Subject: [PATCH 285/375] fixed: - style testing - style fixing --- composer.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 94bfc356..20cf5310 100644 --- a/composer.json +++ b/composer.json @@ -50,9 +50,9 @@ "@test:style", "@test:unit" ], - "test:style": "@php vendor/bin/php-cs-fixer fix --config=.php-cs-fixer.dist.php --allow-risky=yes --dry-run --diff --verbose", + "test:style": "@php vendor/bin/pint --test -v", "test:unit": "@php vendor/bin/phpunit", - "fix:style": "@php vendor/bin/php-cs-fixer fix --config=.php-cs-fixer.dist.php --allow-risky=yes --diff --verbose" + "fix:style": "@php vendor/bin/pint -v" }, "minimum-stability": "dev", "prefer-stable": true From 5acb1cdc4ca3c46df15fcbe4abc9e6c4bb35a44c Mon Sep 17 00:00:00 2001 From: Emiel Bom Date: Thu, 16 Mar 2023 13:28:08 +0100 Subject: [PATCH 286/375] updated ReadMe --- README.md | 153 ++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 107 insertions(+), 46 deletions(-) diff --git a/README.md b/README.md index d425e77b..db71fcb3 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,6 @@ RabbitMQ Queue driver for Laravel [![Latest Stable Version](https://poser.pugx.org/vladimir-yuldashev/laravel-queue-rabbitmq/v/stable?format=flat-square)](https://packagist.org/packages/vladimir-yuldashev/laravel-queue-rabbitmq) [![Build Status](https://github.com/vyuldashev/laravel-queue-rabbitmq/workflows/Tests/badge.svg)](https://github.com/vyuldashev/laravel-queue-rabbitmq/actions) [![Total Downloads](https://poser.pugx.org/vladimir-yuldashev/laravel-queue-rabbitmq/downloads?format=flat-square)](https://packagist.org/packages/vladimir-yuldashev/laravel-queue-rabbitmq) -[![StyleCI](https://styleci.io/repos/14976752/shield)](https://styleci.io/repos/14976752) [![License](https://poser.pugx.org/vladimir-yuldashev/laravel-queue-rabbitmq/license?format=flat-square)](https://packagist.org/packages/vladimir-yuldashev/laravel-queue-rabbitmq) ## Support Policy @@ -24,8 +23,12 @@ composer require vladimir-yuldashev/laravel-queue-rabbitmq The package will automatically register itself. +### Configuration + Add connection to `config/queue.php`: +> This is the minimal config for the rabbitMQ connection/driver to work. + ```php 'connections' => [ // ... @@ -33,9 +36,6 @@ Add connection to `config/queue.php`: 'rabbitmq' => [ 'driver' => 'rabbitmq', - 'queue' => env('RABBITMQ_QUEUE', 'default'), - 'connection' => PhpAmqpLib\Connection\AMQPLazyConnection::class, - 'hosts' => [ [ 'host' => env('RABBITMQ_HOST', '127.0.0.1'), @@ -44,33 +44,17 @@ Add connection to `config/queue.php`: 'password' => env('RABBITMQ_PASSWORD', 'guest'), 'vhost' => env('RABBITMQ_VHOST', '/'), ], + // ... ], - - 'options' => [ - 'ssl_options' => [ - 'cafile' => env('RABBITMQ_SSL_CAFILE', null), - 'local_cert' => env('RABBITMQ_SSL_LOCALCERT', null), - 'local_key' => env('RABBITMQ_SSL_LOCALKEY', null), - 'verify_peer' => env('RABBITMQ_SSL_VERIFY_PEER', true), - 'passphrase' => env('RABBITMQ_SSL_PASSPHRASE', null), - ], - 'queue' => [ - 'job' => VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Jobs\RabbitMQJob::class, - ], - ], - - /* - * Set to "horizon" if you wish to use Laravel Horizon. - */ - 'worker' => env('RABBITMQ_WORKER', 'default'), - 'after_commit' => false, + + // ... ], // ... ], ``` -### Optional Config +### Optional Queue Config Optionally add queue options to the config of a connection. Every queue created for this connection, gets the properties. @@ -164,6 +148,30 @@ by adding extra options. ], ``` +### Horizon support +Starting with 8.0, this package supports [Laravel Horizon](http://horizon.laravel.com) out of the box. Firstly, install +Horizon and then set `RABBITMQ_WORKER` to `horizon`. + +Horizon is depending on events dispatched by the worker. +These events inform Horizon what was done with the message/job. + +This Library supports Horizon, but in the config you have to inform Laravel to use the QueueApi compatible with horizon. + +```php +'connections' => [ + // ... + + 'rabbitmq' => [ + // ... + + /* Set to "horizon" if you wish to use Laravel Horizon. */ + 'worker' => env('RABBITMQ_WORKER', 'default'), + ], + + // ... +], +``` + ### Use your own RabbitMQJob class Sometimes you have to work with messages published by another application. @@ -254,9 +262,74 @@ class RabbitMQJob extends BaseJob } ``` +### Use your own Connection + +You can extend the build-in `PhpAmqpLib\Connection\AMQPStreamConnection::class` +or `PhpAmqpLib\Connection\AMQPSLLConnection::class` and within the connection config, you can define your own class. +When you specify a `connection` key in the config, with your own class name, every connection will use your own class. + +An example for the config: + +```php +'connections' => [ + // ... + + 'rabbitmq' => [ + // ... + + 'connection' = > \App\Queue\Connection\MyRabbitMQConnection::class, + ], + + // ... +], +``` + +### Default Queue + +The connection does use a default queue with value 'default', when no queue is provided by laravel. +It is possible to change te default queue by adding an extra parameter in the connection config. + +```php +'connections' => [ + // ... + + 'rabbitmq' => [ + // ... + + 'queue' => env('RABBITMQ_QUEUE', 'default'), + ], + + // ... +], +``` + +### Heartbeat + +By default, your connection will be created with a heartbeat setting of `0`. +You can alter the heartbeat settings by changing the config. + +```php + +'connections' => [ + // ... + + 'rabbitmq' => [ + // ... + + 'options' => [ + // ... + + 'heartbeat' => 10, + ], + ], + + // ... +], +``` + ### SSL Secure -When u need a secure connection to rabbitMQ server(s). Then this is posible by settitng extra config options. +When u need a secure connection to rabbitMQ server(s). Then this is possible by setting extra config options. ```php 'connections' => [ @@ -283,10 +356,9 @@ When u need a secure connection to rabbitMQ server(s). Then this is posible by s ], ``` -### Lazy Connection +### Events after Database commits -By default, your connection will be created as a lazy connection. -If for some reason you dont want the connection lazy you can turn it off by setting the following config. +To instruct Laravel workers to dispatch events after all database commits are completed. ```php 'connections' => [ @@ -295,32 +367,26 @@ If for some reason you dont want the connection lazy you can turn it off by sett 'rabbitmq' => [ // ... - 'lazy' = > false, + 'after_commit' => true, ], // ... ], ``` -### Heartbeat +### Lazy Connection -By default, your connection will be created with a heartbeat setting of `0`. -You can alter the heartbeat settings by changing the config. +By default, your connection will be created as a lazy connection. +If for some reason you don't want the connection lazy you can turn it off by setting the following config. ```php - 'connections' => [ // ... 'rabbitmq' => [ // ... - 'secure' = > true, - 'options' => [ - // ... - - 'heartbeat' => 10, - ], + 'lazy' = > false, ], // ... @@ -334,11 +400,6 @@ to change anything else. If you do not know how to use the Queue API, please refer to the official Laravel documentation: http://laravel.com/docs/queues -## Laravel Horizon Usage - -Starting with 8.0, this package supports [Laravel Horizon](http://horizon.laravel.com) out of the box. Firstly, install -Horizon and then set `RABBITMQ_WORKER` to `horizon`. - ## Lumen Usage For Lumen usage the service provider should be registered manually as follow in `bootstrap/app.php`: @@ -361,7 +422,7 @@ There are two ways of consuming messages. Setup RabbitMQ using `docker-compose`: ```bash -docker-compose up -d rabbitmq +docker compose up -d ``` To run the test suite you can use the following commands: @@ -378,7 +439,7 @@ composer test:unit ``` If you receive any errors from the style tests, you can automatically fix most, -if not all of the issues with the following command: +if not all the issues with the following command: ```bash composer fix:style From 19b5cbb91db88b330be7a90b13631411052731cd Mon Sep 17 00:00:00 2001 From: Emiel Bom Date: Thu, 16 Mar 2023 13:36:09 +0100 Subject: [PATCH 287/375] updated ReadMe --- README.md | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index db71fcb3..09522ba9 100644 --- a/README.md +++ b/README.md @@ -149,7 +149,9 @@ by adding extra options. ``` ### Horizon support -Starting with 8.0, this package supports [Laravel Horizon](http://horizon.laravel.com) out of the box. Firstly, install + +Starting with 8.0, this package supports [Laravel Horizon](https://laravel.com/docs/horizon) out of the box. Firstly, +install Horizon and then set `RABBITMQ_WORKER` to `horizon`. Horizon is depending on events dispatched by the worker. @@ -172,6 +174,30 @@ This Library supports Horizon, but in the config you have to inform Laravel to u ], ``` +### Octane support + +Starting with 13.3.0, this package supports [Laravel Octane](https://laravel.com/docs/octane) out of the box. Firstly, +install +Octane and then set `RABBITMQ_WORKER` to `octane`. + +This Library supports Octane, but in the config you have to inform Laravel to use the QueueApi compatible with octane. + +> Note: don't forget to warm 'rabbitmq' connection in the octane config. +```php +'connections' => [ + // ... + + 'rabbitmq' => [ + // ... + + /* Set to "octane" if you wish to use Laravel Octane. */ + 'worker' => env('RABBITMQ_WORKER', 'default'), + ], + + // ... +], +``` + ### Use your own RabbitMQJob class Sometimes you have to work with messages published by another application. From ce16e57ffaf50255dd243cbf9d9af0b9da074616 Mon Sep 17 00:00:00 2001 From: Emiel Bom Date: Thu, 16 Mar 2023 13:40:32 +0100 Subject: [PATCH 288/375] Revert "removed unnecessary connection config" This reverts commit 7897f77654ea0a5dcf1ec43239b88b00f948ba2c. --- tests/Functional/TestCase.php | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/Functional/TestCase.php b/tests/Functional/TestCase.php index 4edc8853..b57fd768 100644 --- a/tests/Functional/TestCase.php +++ b/tests/Functional/TestCase.php @@ -157,6 +157,24 @@ protected function getEnvironmentSetUp($app): void 'worker' => 'default', ]); + $app['config']->set('queue.connections.rabbitmq-with-octane-reconnect-options', [ + 'driver' => 'rabbitmq', + 'queue' => 'default', + 'connection' => 'default', + + 'hosts' => [ + [ + 'host' => getenv('HOST'), + 'port' => getenv('PORT'), + 'vhost' => '/', + 'user' => 'guest', + 'password' => 'guest', + ], + ], + + 'worker' => 'octane', + + ]); } /** From eefd94befec4659b3df0f34fe9ec047108597384 Mon Sep 17 00:00:00 2001 From: Emiel Bom Date: Thu, 16 Mar 2023 13:45:00 +0100 Subject: [PATCH 289/375] really... bump --- tests/Functional/RabbitMQQueueTest.php | 2 +- tests/Functional/TestCase.php | 7 +------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/tests/Functional/RabbitMQQueueTest.php b/tests/Functional/RabbitMQQueueTest.php index fac5e889..4c1aefab 100644 --- a/tests/Functional/RabbitMQQueueTest.php +++ b/tests/Functional/RabbitMQQueueTest.php @@ -21,7 +21,7 @@ public function testConnection(): void $queue = $this->connection('rabbitmq-with-options-empty'); $this->assertInstanceOf(RabbitMQQueue::class, $queue); - $queue = $this->connection('rabbitmq-with-octane-reconnect-options'); + $queue = $this->connection('rabbitmq-for-octane'); $this->assertInstanceOf(RabbitMQQueue::class, $queue); $this->assertInstanceOf(OctaneRabbitMQQueue::class, $queue); } diff --git a/tests/Functional/TestCase.php b/tests/Functional/TestCase.php index b57fd768..73cb2af2 100644 --- a/tests/Functional/TestCase.php +++ b/tests/Functional/TestCase.php @@ -157,11 +157,8 @@ protected function getEnvironmentSetUp($app): void 'worker' => 'default', ]); - $app['config']->set('queue.connections.rabbitmq-with-octane-reconnect-options', [ + $app['config']->set('queue.connections.rabbitmq-for-octane', [ 'driver' => 'rabbitmq', - 'queue' => 'default', - 'connection' => 'default', - 'hosts' => [ [ 'host' => getenv('HOST'), @@ -171,9 +168,7 @@ protected function getEnvironmentSetUp($app): void 'password' => 'guest', ], ], - 'worker' => 'octane', - ]); } From 08fae3a4f93a9c243c8367101deb4bc80132d60e Mon Sep 17 00:00:00 2001 From: Emiel Bom Date: Fri, 17 Mar 2023 17:18:36 +0100 Subject: [PATCH 290/375] refactor getExchangeType() - Prior to PHP 8.0 constant() returned null and an E_WARNING, so default could be set easily. Sinds PHP 8.0 an ERROR exception is thrown. - added a defined() check. --- src/Queue/RabbitMQQueue.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index 8c67cc6d..8dded6b1 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -640,7 +640,9 @@ protected function getRoutingKey(string $destination): string */ protected function getExchangeType(?string $type = null): string { - return @constant(AMQPExchangeType::class.'::'.Str::upper($type ?: $this->getConfig()->getExchangeType())) ?: AMQPExchangeType::DIRECT; + $constant = AMQPExchangeType::class.'::'.Str::upper($type ?: $this->getConfig()->getExchangeType()); + + return defined($constant) ? constant($constant) : AMQPExchangeType::DIRECT; } /** From 9a900f787823ef11b4b29691a33106c8fe9996ac Mon Sep 17 00:00:00 2001 From: Emiel Bom Date: Fri, 17 Mar 2023 17:23:13 +0100 Subject: [PATCH 291/375] altered tests - added null values in testing config - testing defaults - testing logic --- tests/Functional/RabbitMQQueueTest.php | 74 ++++++++++++++++++++++---- tests/Functional/TestCase.php | 40 ++++++++++++++ 2 files changed, 105 insertions(+), 9 deletions(-) diff --git a/tests/Functional/RabbitMQQueueTest.php b/tests/Functional/RabbitMQQueueTest.php index 5a50342b..7a106a08 100644 --- a/tests/Functional/RabbitMQQueueTest.php +++ b/tests/Functional/RabbitMQQueueTest.php @@ -31,6 +31,9 @@ public function testConfigRerouteFailed(): void $queue = $this->connection('rabbitmq-with-options-empty'); $this->assertFalse($this->callProperty($queue, 'config')->isRerouteFailed()); + + $queue = $this->connection('rabbitmq-with-options-null'); + $this->assertFalse($this->callProperty($queue, 'config')->isRerouteFailed()); } public function testConfigPrioritizeDelayed(): void @@ -43,6 +46,9 @@ public function testConfigPrioritizeDelayed(): void $queue = $this->connection('rabbitmq-with-options-empty'); $this->assertFalse($this->callProperty($queue, 'config')->isPrioritizeDelayed()); + + $queue = $this->connection('rabbitmq-with-options-null'); + $this->assertFalse($this->callProperty($queue, 'config')->isPrioritizeDelayed()); } public function testQueueMaxPriority(): void @@ -58,6 +64,10 @@ public function testQueueMaxPriority(): void $queue = $this->connection('rabbitmq-with-options-empty'); $this->assertIsInt($this->callProperty($queue, 'config')->getQueueMaxPriority()); $this->assertSame(2, $this->callProperty($queue, 'config')->getQueueMaxPriority()); + + $queue = $this->connection('rabbitmq-with-options-null'); + $this->assertIsInt($this->callProperty($queue, 'config')->getQueueMaxPriority()); + $this->assertSame(2, $this->callProperty($queue, 'config')->getQueueMaxPriority()); } public function testConfigExchangeType(): void @@ -69,45 +79,78 @@ public function testConfigExchangeType(): void $queue = $this->connection('rabbitmq-with-options'); $this->assertSame(AMQPExchangeType::TOPIC, $this->callMethod($queue, 'getExchangeType')); + $this->assertSame(AMQPExchangeType::DIRECT, $this->callMethod($queue, 'getExchangeType', ['direct'])); $queue = $this->connection('rabbitmq-with-options-empty'); $this->assertSame(AMQPExchangeType::DIRECT, $this->callMethod($queue, 'getExchangeType')); + + $queue = $this->connection('rabbitmq-with-options-null'); + $this->assertSame(AMQPExchangeType::DIRECT, $this->callMethod($queue, 'getExchangeType')); + + //testing an unkown type with a default + $this->callProperty($queue, 'config')->setExchangeType('unknown'); + $this->assertSame(AMQPExchangeType::DIRECT, $this->callMethod($queue, 'getExchangeType')); } public function testExchange(): void { $queue = $this->connection(); $this->assertSame('test', $this->callMethod($queue, 'getExchange', ['test'])); - $this->assertNull($this->callMethod($queue, 'getExchange', [''])); - $this->assertNull($this->callMethod($queue, 'getExchange')); + $this->assertSame('', $this->callMethod($queue, 'getExchange', [''])); + $this->assertSame('', $this->callMethod($queue, 'getExchange', [null])); + $this->assertSame('', $this->callMethod($queue, 'getExchange')); $queue = $this->connection('rabbitmq-with-options'); - $this->assertNotNull($this->callMethod($queue, 'getExchange')); $this->assertSame('application-x', $this->callMethod($queue, 'getExchange')); + $this->assertSame('application-x', $this->callMethod($queue, 'getExchange', [null])); + $this->assertSame('test', $this->callMethod($queue, 'getExchange', ['test'])); + $this->assertSame('', $this->callMethod($queue, 'getExchange', [''])); $queue = $this->connection('rabbitmq-with-options-empty'); - $this->assertNull($this->callMethod($queue, 'getExchange')); + $this->assertSame('', $this->callMethod($queue, 'getExchange')); + $this->assertSame('', $this->callMethod($queue, 'getExchange', [null])); + $this->assertSame('test', $this->callMethod($queue, 'getExchange', ['test'])); + $this->assertSame('', $this->callMethod($queue, 'getExchange', [''])); + + $queue = $this->connection('rabbitmq-with-options-null'); + $this->assertSame('', $this->callMethod($queue, 'getExchange')); + $this->assertSame('', $this->callMethod($queue, 'getExchange', [null])); + $this->assertSame('test', $this->callMethod($queue, 'getExchange', ['test'])); + $this->assertSame('', $this->callMethod($queue, 'getExchange', [''])); } public function testFailedExchange(): void { $queue = $this->connection(); $this->assertSame('test', $this->callMethod($queue, 'getFailedExchange', ['test'])); - $this->assertNull($this->callMethod($queue, 'getExchange', [''])); - $this->assertNull($this->callMethod($queue, 'getFailedExchange')); + $this->assertSame('', $this->callMethod($queue, 'getFailedExchange', [''])); + $this->assertSame('', $this->callMethod($queue, 'getFailedExchange', [null])); + $this->assertSame('', $this->callMethod($queue, 'getFailedExchange')); $queue = $this->connection('rabbitmq-with-options'); - $this->assertNotNull($this->callMethod($queue, 'getFailedExchange')); $this->assertSame('failed-exchange', $this->callMethod($queue, 'getFailedExchange')); + $this->assertSame('failed-exchange', $this->callMethod($queue, 'getFailedExchange', [null])); + $this->assertSame('test', $this->callMethod($queue, 'getFailedExchange', ['test'])); + $this->assertSame('', $this->callMethod($queue, 'getFailedExchange', [''])); $queue = $this->connection('rabbitmq-with-options-empty'); - $this->assertNull($this->callMethod($queue, 'getFailedExchange')); + $this->assertSame('', $this->callMethod($queue, 'getFailedExchange')); + $this->assertSame('', $this->callMethod($queue, 'getFailedExchange', [null])); + $this->assertSame('test', $this->callMethod($queue, 'getFailedExchange', ['test'])); + $this->assertSame('', $this->callMethod($queue, 'getFailedExchange', [''])); + + $queue = $this->connection('rabbitmq-with-options-null'); + $this->assertSame('', $this->callMethod($queue, 'getFailedExchange')); + $this->assertSame('', $this->callMethod($queue, 'getFailedExchange', [null])); + $this->assertSame('test', $this->callMethod($queue, 'getFailedExchange', ['test'])); + $this->assertSame('', $this->callMethod($queue, 'getFailedExchange', [''])); } public function testRoutingKey(): void { $queue = $this->connection(); $this->assertSame('test', $this->callMethod($queue, 'getRoutingKey', ['test'])); + $this->assertSame('test', $this->callMethod($queue, 'getRoutingKey', ['.test'])); $this->assertSame('', $this->callMethod($queue, 'getRoutingKey', [''])); $queue = $this->connection('rabbitmq-with-options'); @@ -115,13 +158,18 @@ public function testRoutingKey(): void $queue = $this->connection('rabbitmq-with-options-empty'); $this->assertSame('test', $this->callMethod($queue, 'getRoutingKey', ['test'])); + + $queue = $this->connection('rabbitmq-with-options-null'); + $this->assertSame('test', $this->callMethod($queue, 'getRoutingKey', ['test'])); + $this->callProperty($queue, 'config')->setExchangeRoutingKey('.an.alternate.routing-key'); + $this->assertSame('an.alternate.routing-key', $this->callMethod($queue, 'getRoutingKey', ['test'])); } public function testFailedRoutingKey(): void { $queue = $this->connection(); - $this->assertSame('test.failed', $this->callMethod($queue, 'getFailedRoutingKey', ['test'])); + $this->assertSame('test.failed', $this->callMethod($queue, 'getFailedRoutingKey', ['.test'])); $this->assertSame('failed', $this->callMethod($queue, 'getFailedRoutingKey', [''])); $queue = $this->connection('rabbitmq-with-options'); @@ -129,6 +177,11 @@ public function testFailedRoutingKey(): void $queue = $this->connection('rabbitmq-with-options-empty'); $this->assertSame('test.failed', $this->callMethod($queue, 'getFailedRoutingKey', ['test'])); + + $queue = $this->connection('rabbitmq-with-options-null'); + $this->assertSame('test.failed', $this->callMethod($queue, 'getFailedRoutingKey', ['test'])); + $this->callProperty($queue, 'config')->setFailedRoutingKey('.an.alternate.routing-key'); + $this->assertSame('an.alternate.routing-key', $this->callMethod($queue, 'getFailedRoutingKey', ['test'])); } public function testConfigQuorum(): void @@ -142,6 +195,9 @@ public function testConfigQuorum(): void $queue = $this->connection('rabbitmq-with-options-empty'); $this->assertFalse($this->callProperty($queue, 'config')->isQuorum()); + $queue = $this->connection('rabbitmq-with-options-null'); + $this->assertFalse($this->callProperty($queue, 'config')->isQuorum()); + $queue = $this->connection('rabbitmq-with-quorum-options'); $this->assertTrue($this->callProperty($queue, 'config')->isQuorum()); } diff --git a/tests/Functional/TestCase.php b/tests/Functional/TestCase.php index 4edc8853..0e0f747d 100644 --- a/tests/Functional/TestCase.php +++ b/tests/Functional/TestCase.php @@ -118,6 +118,46 @@ protected function getEnvironmentSetUp($app): void 'worker' => 'default', + ]); + $app['config']->set('queue.connections.rabbitmq-with-options-null', [ + 'driver' => 'rabbitmq', + 'queue' => 'order', + 'connection' => 'default', + + 'hosts' => [ + [ + 'host' => null, + 'port' => null, + 'vhost' => null, + 'user' => null, + 'password' => null, + ], + ], + + 'options' => [ + 'ssl_options' => [ + 'cafile' => null, + 'local_cert' => null, + 'local_key' => null, + 'verify_peer' => null, + 'passphrase' => null, + ], + + 'queue' => [ + 'prioritize_delayed' => null, + 'queue_max_priority' => null, + 'exchange' => null, + 'exchange_type' => null, + 'exchange_routing_key' => null, + 'reroute_failed' => null, + 'failed_exchange' => null, + 'failed_routing_key' => null, + 'quorum' => null, + ], + ], + + 'worker' => 'default', + ]); $app['config']->set('queue.connections.rabbitmq-with-quorum-options', [ 'driver' => 'rabbitmq', From bf18827d3624c0493949c86fb95dfd428cd31a31 Mon Sep 17 00:00:00 2001 From: Emiel Bom Date: Fri, 17 Mar 2023 17:28:37 +0100 Subject: [PATCH 292/375] some extra cleanup --- src/Queue/Connection/ConfigFactory.php | 8 +- src/Queue/QueueConfig.php | 143 ++++++++++++++++++------- src/Queue/RabbitMQQueue.php | 15 ++- 3 files changed, 114 insertions(+), 52 deletions(-) diff --git a/src/Queue/Connection/ConfigFactory.php b/src/Queue/Connection/ConfigFactory.php index 4b8f87a1..c6e24699 100644 --- a/src/Queue/Connection/ConfigFactory.php +++ b/src/Queue/Connection/ConfigFactory.php @@ -20,14 +20,14 @@ public static function make(array $config = []): AMQPConnectionConfig // Set the connection to a Lazy by default $connectionConfig->setIsLazy(! in_array( Arr::get($config, 'lazy') ?? true, - [false, 0, '0', 'false'], + [false, 0, '0', 'false', 'no'], true) ); // Set the connection to unsecure by default $connectionConfig->setIsSecure(in_array( Arr::get($config, 'secure'), - [true, 1, '1', 'true'], + [true, 1, '1', 'true', 'yes'], true) ); @@ -86,8 +86,8 @@ protected static function getHeartbeatFromConfig(AMQPConnectionConfig $connectio { $heartbeat = Arr::get($config, self::CONFIG_OPTIONS.'.heartbeat'); - if (! empty($heartbeat) && is_numeric($heartbeat) && 0 < (int) $heartbeat) { - $connectionConfig->setHeartbeat($heartbeat); + if (is_numeric($heartbeat) && intval($heartbeat) > 0) { + $connectionConfig->setHeartbeat((int) $heartbeat); } } } diff --git a/src/Queue/QueueConfig.php b/src/Queue/QueueConfig.php index b5aea131..e7ec27c4 100644 --- a/src/Queue/QueueConfig.php +++ b/src/Queue/QueueConfig.php @@ -16,7 +16,7 @@ class QueueConfig protected int $queueMaxPriority = 2; - protected ?string $exchange = null; + protected string $exchange = ''; protected string $exchangeType = 'direct'; @@ -24,7 +24,7 @@ class QueueConfig protected bool $rerouteFailed = false; - protected ?string $failedExchange = null; + protected string $failedExchange = ''; protected string $failedRoutingKey = '%s.failed'; @@ -32,18 +32,28 @@ class QueueConfig protected array $options = []; + /** + * Holds the default queue name + * + * When no queue name is provided by laravel queue / workers via the QueueApi method's, + * this value is used to publish messages. + */ public function getQueue(): string { return $this->queue; } - public function setQueue(?string $queue): QueueConfig + public function setQueue(string $queue): QueueConfig { - $this->queue = $queue ?: 'default'; + $this->queue = $queue; return $this; } + /** + * Returns &true; as indication that jobs should be dispatched after all database transactions + * have been committed. + */ public function isDispatchAfterCommit(): bool { return $this->dispatchAfterCommit; @@ -51,37 +61,38 @@ public function isDispatchAfterCommit(): bool public function setDispatchAfterCommit($dispatchAfterCommit): QueueConfig { - $this->dispatchAfterCommit = ! empty($dispatchAfterCommit); - - return $this; - } - - public function getOptions(): array - { - return $this->options; - } - - public function setOptions(?array $options): QueueConfig - { - $this->options = $options ?: []; + $this->dispatchAfterCommit = $this->toBoolean($dispatchAfterCommit); return $this; } + /** + * Get the Job::class to use when processing messages + */ public function getAbstractJob(): string { return $this->abstractJob; } - public function setAbstractJob(?string $abstract): QueueConfig + public function setAbstractJob(string $abstract): QueueConfig { - $this->abstractJob = $abstract ?: RabbitMQJob::class; + $this->abstractJob = $abstract; return $this; } /** * Returns &true;, if delayed messages should be prioritized. + * + * RabbitMQ queues work with the FIFO method. So when there are 10000 messages in the queue and + * the delayed message is put back to the queue (at the end) for further processing the delayed message won´t + * process before all 10000 messages are processed. The same is true for requeueing. + * + * This may not what you desire. + * When you want the message to get processed immediately after the delayed time expires or when requeueing, we can + * use prioritization. + * + * @see[https://www.rabbitmq.com/queues.html#basics] */ public function isPrioritizeDelayed(): bool { @@ -90,7 +101,7 @@ public function isPrioritizeDelayed(): bool public function setPrioritizeDelayed($prioritizeDelayed): QueueConfig { - $this->prioritizeDelayed = ! empty($prioritizeDelayed); + $this->prioritizeDelayed = $this->toBoolean($prioritizeDelayed); return $this; } @@ -109,7 +120,7 @@ public function getQueueMaxPriority(): int public function setQueueMaxPriority($queueMaxPriority): QueueConfig { - if (is_numeric($queueMaxPriority)) { + if (is_numeric($queueMaxPriority) && intval($queueMaxPriority) > 1) { $this->queueMaxPriority = (int) $queueMaxPriority; } @@ -117,43 +128,59 @@ public function setQueueMaxPriority($queueMaxPriority): QueueConfig } /** - * Get the exchange name, or &null; as default value. + * Get the exchange name, or empty string; as default value. + * + * The default exchange is an unnamed pre-declared direct exchange. Usually, an empty string + * is frequently used to indicate it. If you choose default exchange, your message will be delivered + * to a queue with the same name as the routing key. + * With a routing key that is the same as the queue name, every queue is immediately tied to the default exchange. */ - public function getExchange(): ?string + public function getExchange(): string { return $this->exchange; } - public function setExchange(?string $exchange): QueueConfig + public function setExchange(string $exchange): QueueConfig { - $this->exchange = $exchange ?: null; + $this->exchange = $exchange; return $this; } + /** + * Get the exchange type + * + * There are four basic RabbitMQ exchange types in RabbitMQ, each of which uses different parameters + * and bindings to route messages in various ways, These are: 'direct', 'topic', 'fanout', 'headers' + * + * The default type is set as 'direct' + */ public function getExchangeType(): string { return $this->exchangeType; } - public function setExchangeType(?string $exchangeType): QueueConfig + public function setExchangeType(string $exchangeType): QueueConfig { - $this->exchangeType = $exchangeType ?: 'direct'; + $this->exchangeType = $exchangeType; return $this; } /** - * @return string + * Get the routing key when using an exchange other than the direct exchange. + * The routing key is a message attribute taken into account by the exchange when deciding how to route a message. + * + * The default routing-key is the given destination: '%s'. */ - public function getExchangeRoutingKey(): ?string + public function getExchangeRoutingKey(): string { return $this->exchangeRoutingKey; } - public function setExchangeRoutingKey(?string $exchangeRoutingKey): QueueConfig + public function setExchangeRoutingKey(string $exchangeRoutingKey): QueueConfig { - $this->exchangeRoutingKey = $exchangeRoutingKey ?: '%s'; + $this->exchangeRoutingKey = $exchangeRoutingKey; return $this; } @@ -168,39 +195,46 @@ public function isRerouteFailed(): bool public function setRerouteFailed($rerouteFailed): QueueConfig { - $this->rerouteFailed = ! empty($rerouteFailed); + $this->rerouteFailed = $this->toBoolean($rerouteFailed); return $this; } - public function getFailedExchange(): ?string + /** + * Get the exchange name with messages are published against. + * The default exchange is empty, so messages will be published directly to a queue. + */ + public function getFailedExchange(): string { return $this->failedExchange; } - public function setFailedExchange(?string $failedExchange): QueueConfig + public function setFailedExchange(string $failedExchange): QueueConfig { - $this->failedExchange = $failedExchange ?: null; + $this->failedExchange = $failedExchange; return $this; } /** - * Get the routing-key for failed messages - * The default routing-key is the given destination substituted by '.failed'. + * Get the substitution string for failed messages + * The default routing-key is the given destination substituted by '%s.failed'. */ public function getFailedRoutingKey(): string { return $this->failedRoutingKey; } - public function setFailedRoutingKey(?string $failedRoutingKey): QueueConfig + public function setFailedRoutingKey(string $failedRoutingKey): QueueConfig { - $this->failedRoutingKey = $failedRoutingKey ?: '%s.failed'; + $this->failedRoutingKey = $failedRoutingKey; return $this; } + /** + * Returns &true;, if queue is marked or set as quorum queue. + */ public function isQuorum(): bool { return $this->quorum; @@ -208,8 +242,37 @@ public function isQuorum(): bool public function setQuorum($quorum): QueueConfig { - $this->quorum = ! empty($quorum); + $this->quorum = $this->toBoolean($quorum); + + return $this; + } + + /** + * Holds all unknown queue options provided in the connection config + */ + public function getOptions(): array + { + return $this->options; + } + + public function setOptions(array $options): QueueConfig + { + $this->options = $options; return $this; } + + /** + * Filters $value to boolean value + * + * Returns: &true; + * For values: 1, '1', true, 'true', 'yes' + * + * Returns: &false; + * For values: 0, '0', false, 'false', '', null, [] , 'ok', 'no', 'no not a bool', 'yes a bool' + */ + protected function toBoolean($value): bool + { + return filter_var($value, FILTER_VALIDATE_BOOLEAN); + } } diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index 8dded6b1..519bf8b5 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -594,7 +594,7 @@ protected function getQueueArguments(string $destination): array } if ($this->getConfig()->isRerouteFailed()) { - $arguments['x-dead-letter-exchange'] = $this->getFailedExchange() ?? ''; + $arguments['x-dead-letter-exchange'] = $this->getFailedExchange(); $arguments['x-dead-letter-routing-key'] = $this->getFailedRoutingKey($destination); } @@ -611,7 +611,7 @@ protected function getQueueArguments(string $destination): array protected function getDelayQueueArguments(string $destination, int $ttl): array { return [ - 'x-dead-letter-exchange' => $this->getExchange() ?? '', + 'x-dead-letter-exchange' => $this->getExchange(), 'x-dead-letter-routing-key' => $this->getRoutingKey($destination), 'x-message-ttl' => $ttl, 'x-expires' => $ttl * 2, @@ -619,11 +619,11 @@ protected function getDelayQueueArguments(string $destination, int $ttl): array } /** - * Get the exchange name, or &null; as default value. + * Get the exchange name, or empty string; as default value. */ - protected function getExchange(string $exchange = null): ?string + protected function getExchange(?string $exchange = null): string { - return $exchange ?: $this->getConfig()->getExchange(); + return $exchange ?? $this->getConfig()->getExchange(); } /** @@ -648,9 +648,9 @@ protected function getExchangeType(?string $type = null): string /** * Get the exchange for failed messages. */ - protected function getFailedExchange(string $exchange = null): ?string + protected function getFailedExchange(?string $exchange = null): string { - return $exchange ?: $this->getConfig()->getFailedExchange(); + return $exchange ?? $this->getConfig()->getFailedExchange(); } /** @@ -681,7 +681,6 @@ protected function isQueueDeclared(string $name): bool /** * Declare the destination when necessary. * - * * @throws AMQPProtocolChannelException */ protected function declareDestination(string $destination, ?string $exchange = null, string $exchangeType = AMQPExchangeType::DIRECT): void From fa26c9cefa2aac160713de05a20547553b030c20 Mon Sep 17 00:00:00 2001 From: Emiel Bom <11626777+adm-bome@users.noreply.github.com> Date: Fri, 17 Mar 2023 17:36:18 +0100 Subject: [PATCH 293/375] Update README.md Co-authored-by: Matt Grande --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index db71fcb3..aaff3441 100644 --- a/README.md +++ b/README.md @@ -264,7 +264,7 @@ class RabbitMQJob extends BaseJob ### Use your own Connection -You can extend the build-in `PhpAmqpLib\Connection\AMQPStreamConnection::class` +You can extend the built-in `PhpAmqpLib\Connection\AMQPStreamConnection::class` or `PhpAmqpLib\Connection\AMQPSLLConnection::class` and within the connection config, you can define your own class. When you specify a `connection` key in the config, with your own class name, every connection will use your own class. From a35587d50f86a8825397a9b17d2c9b8caceae248 Mon Sep 17 00:00:00 2001 From: Emiel Bom <11626777+adm-bome@users.noreply.github.com> Date: Fri, 17 Mar 2023 17:36:27 +0100 Subject: [PATCH 294/375] Update README.md Co-authored-by: Matt Grande --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index aaff3441..c9d21614 100644 --- a/README.md +++ b/README.md @@ -329,7 +329,7 @@ You can alter the heartbeat settings by changing the config. ### SSL Secure -When u need a secure connection to rabbitMQ server(s). Then this is possible by setting extra config options. +If you need a secure connection to rabbitMQ server(s), you will need to add these extra config options. ```php 'connections' => [ From 983d53d0fa418026f2ebe35c7826d5e955661b66 Mon Sep 17 00:00:00 2001 From: Emiel Bom <11626777+adm-bome@users.noreply.github.com> Date: Fri, 17 Mar 2023 20:25:42 +0100 Subject: [PATCH 295/375] Update README.md --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index c9d21614..5065e013 100644 --- a/README.md +++ b/README.md @@ -395,8 +395,7 @@ If for some reason you don't want the connection lazy you can turn it off by set ## Laravel Usage -Once you completed the configuration you can use the Laravel Queue API. If you used other queue drivers you do not need -to +Once you completed the configuration you can use the Laravel Queue API. If you used other queue drivers you do not need to change anything else. If you do not know how to use the Queue API, please refer to the official Laravel documentation: http://laravel.com/docs/queues From cb0defca92c9f467dfa70d1d15c0819b85bdfb73 Mon Sep 17 00:00:00 2001 From: Daniil Date: Wed, 22 Mar 2023 03:49:29 +0800 Subject: [PATCH 296/375] Add job example for handling non-json messages from rabbitmq queue --- README.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/README.md b/README.md index 24c25862..e459bf68 100644 --- a/README.md +++ b/README.md @@ -254,6 +254,32 @@ class RabbitMQJob extends BaseJob } ``` +If you want to handle raw message, not in JSON format or without 'job' key in JSON, you should add stub for `getName` method: +```php +getRawBody(); + Log::info($anyMessage); + + $this->delete(); + } + + public function getName() + { + return ''; + } +} +``` + ## Laravel Usage Once you completed the configuration you can use the Laravel Queue API. If you used other queue drivers you do not need to From 5b8508bfaa42a9ad2333980f89b9521b6b8eb2f6 Mon Sep 17 00:00:00 2001 From: Emiel Bom Date: Tue, 28 Mar 2023 10:53:14 +0200 Subject: [PATCH 297/375] extracted new channel lines to a method createChannel(). --- src/Queue/RabbitMQQueue.php | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index 519bf8b5..72e7c352 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -84,7 +84,7 @@ public function size($queue = null): int } // create a temporary channel, so the main channel will not be closed on exception - $channel = $this->getConnection()->channel(); + $channel = $this->createChannel(); [, $size] = $channel->queue_declare($queue, true); $channel->close(); @@ -277,7 +277,7 @@ public function setConnection(AbstractConnection $connection): RabbitMQQueue public function getChannel($forceNew = false): AMQPChannel { if (! $this->channel || $forceNew) { - $this->channel = $this->getConnection()->channel(); + $this->channel = $this->createChannel(); } return $this->channel; @@ -327,7 +327,7 @@ public function isExchangeExists(string $exchange): bool try { // create a temporary channel, so the main channel will not be closed on exception - $channel = $this->getConnection()->channel(); + $channel = $this->createChannel(); $channel->exchange_declare($exchange, '', true); $channel->close(); @@ -401,7 +401,7 @@ public function isQueueExists(string $name = null): bool { try { // create a temporary channel, so the main channel will not be closed on exception - $channel = $this->getConnection()->channel(); + $channel = $this->createChannel(); $channel->queue_declare($this->getQueue($name), true); $channel->close(); @@ -476,7 +476,7 @@ public function bindQueue(string $queue, string $exchange, string $routingKey = public function purge(string $queue = null): void { // create a temporary channel, so the main channel will not be closed on exception - $channel = $this->getConnection()->channel(); + $channel = $this->createChannel(); $channel->queue_purge($this->getQueue($queue)); $channel->close(); } @@ -733,4 +733,9 @@ protected function publishBatch(): void { $this->getChannel()->publish_batch(); } + + protected function createChannel(): AMQPChannel + { + return $this->getConnection()->channel(); + } } From b43ac849361f0f87d5c851b764827e7b7a2226b4 Mon Sep 17 00:00:00 2001 From: Emiel Bom Date: Tue, 28 Mar 2023 12:18:56 +0200 Subject: [PATCH 298/375] Keep track of the declared queues inside RabbitMQ. So we won't check unnecessary times. --- src/Queue/RabbitMQQueue.php | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index 72e7c352..47a28eac 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -399,12 +399,20 @@ public function deleteExchange(string $name, bool $unused = false): void */ public function isQueueExists(string $name = null): bool { + $queueName = $this->getQueue($name); + + if ($this->isQueueDeclared($queueName)) { + return true; + } + try { // create a temporary channel, so the main channel will not be closed on exception $channel = $this->createChannel(); - $channel->queue_declare($this->getQueue($name), true); + $channel->queue_declare($queueName, true); $channel->close(); + $this->queues[] = $queueName; + return true; } catch (AMQPProtocolChannelException $exception) { if ($exception->amqp_reply_code === 404) { @@ -451,6 +459,9 @@ public function deleteQueue(string $name, bool $if_unused = false, bool $if_empt return; } + $idx = array_search($name, $this->queues); + unset($this->queues[$idx]); + $this->getChannel()->queue_delete($name, $if_unused, $if_empty); } From 8da5b9d05c67ee0269c1711deb142361fe478416 Mon Sep 17 00:00:00 2001 From: Emiel Bom Date: Tue, 28 Mar 2023 17:23:15 +0200 Subject: [PATCH 299/375] Some rework based on PR #528 - issue #460 - rework based on [comment](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/531#issuecomment-1486849841) --- src/Queue/RabbitMQQueue.php | 49 +++++++++++++++++++++++------------- src/Queue/ReconnectTrait.php | 28 ++++++++++++++++++--- 2 files changed, 56 insertions(+), 21 deletions(-) diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index 07338052..57c23e64 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -183,11 +183,19 @@ public function laterRaw($delay, string $payload, $queue = null, int $attempts = */ public function bulk($jobs, $data = '', $queue = null): void { - foreach ((array) $jobs as $job) { + $this->publishBatch($jobs, $queue, $data); + } + + /** + * @throws AMQPProtocolChannelException + */ + protected function publishBatch($jobs, $data = '', $queue = null): void + { + foreach ($jobs as $job) { $this->bulkRaw($this->createPayload($job, $queue, $data), $queue, ['job' => $job]); } - $this->publishBatch(); + $this->batchPublish(); } /** @@ -274,21 +282,6 @@ public function setConnection(AbstractConnection $connection): RabbitMQQueue return $this; } - public function getChannel($forceNew = false): AMQPChannel - { - if (! $this->channel || $forceNew) { - $this->channel = $this->createChannel(); - } - - return $this->channel; - } - - protected function reconnect() - { - $this->getConnection()->reconnect(); - $this->getChannel(true); - } - /** * Job class to use. * @@ -746,13 +739,33 @@ protected function publishBasic($msg, $exchange = '', $destination = '', $mandat $this->getChannel()->basic_publish($msg, $exchange, $destination, $mandatory, $immediate, $ticket); } - protected function publishBatch(): void + protected function batchPublish(): void { $this->getChannel()->publish_batch(); } + public function getChannel($forceNew = false): AMQPChannel + { + if (! $this->channel || $forceNew) { + $this->channel = $this->createChannel(); + } + + return $this->channel; + } + protected function createChannel(): AMQPChannel { return $this->getConnection()->channel(); } + + /** + * @throws Exception + */ + protected function reconnect(): void + { + // Reconnects using the original connection settings. + $this->getConnection()->reconnect(); + // Create a new main channel because all old channels are removed. + $this->getChannel(true); + } } diff --git a/src/Queue/ReconnectTrait.php b/src/Queue/ReconnectTrait.php index 41ec7395..e88059c5 100644 --- a/src/Queue/ReconnectTrait.php +++ b/src/Queue/ReconnectTrait.php @@ -2,11 +2,16 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Queue; +use Exception; +use PhpAmqpLib\Channel\AMQPChannel; use PhpAmqpLib\Exception\AMQPChannelClosedException; use PhpAmqpLib\Exception\AMQPConnectionClosedException; trait ReconnectTrait { + /** + * @throws Exception + */ protected function publishBasic($msg, $exchange = '', $destination = '', $mandatory = false, $immediate = false, $ticket = null): void { try { @@ -17,13 +22,30 @@ protected function publishBasic($msg, $exchange = '', $destination = '', $mandat } } - protected function publishBatch(): void + /** + * @throws Exception + */ + protected function publishBatch($jobs, $data = '', $queue = null): void { try { - parent::publishBatch(); + parent::publishBatch($jobs, $data, $queue); } catch (AMQPConnectionClosedException|AMQPChannelClosedException) { $this->reconnect(); - parent::publishBatch(); + parent::publishBatch($jobs, $data, $queue); + } + } + + /** + * @throws Exception + */ + protected function createChannel(): AMQPChannel + { + try { + return parent::createChannel(); + } catch (AMQPConnectionClosedException) { + $this->reconnect(); + + return parent::createChannel(); } } } From 46f478082bc021ba2b3c009b870ba6f851cddf3e Mon Sep 17 00:00:00 2001 From: Emiel Bom <11626777+adm-bome@users.noreply.github.com> Date: Wed, 12 Apr 2023 11:38:38 +0200 Subject: [PATCH 300/375] Update CHANGELOG-13x.md Some chagelog lines --- CHANGELOG-13x.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGELOG-13x.md b/CHANGELOG-13x.md index 0631fc9d..d7ee1f60 100644 --- a/CHANGELOG-13x.md +++ b/CHANGELOG-13x.md @@ -4,6 +4,17 @@ All notable changes to this project will be documented in this file. ## [unreleased](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v13.2.0...master) +## [13.3.0](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v13.2.0...13.3.0) + +- Refactor the creation of RabbitMQ Connection and QueueAPI. [#528](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/528) +- Added configuration object as single dependency for RabbitMQQueue in constructor. [#528](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/528) +- Fix method getExchangeType, not throwing an exception. [#528](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/528) +- Separating the apilogic from the actual publishing to RabbitMQ. [#528](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/528) +- Added a reconnect method. [#528](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/528) +- Fix the connection and channel not being fully lazy, when QueueAPI was created. [#528](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/528) +- Keep track of declared queue's within RabbitMQ. [#528](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/528) +- Implemented the 'rest' option to the consumer [#530](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/530) + ## [13.2.0](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v13.1.0...13.2.0) - Compatibility with Laravel 10 [#525](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/525) From 22beb422762349de781b5bb13f56d9202272a4d2 Mon Sep 17 00:00:00 2001 From: Daniil Date: Mon, 17 Apr 2023 22:41:45 +0800 Subject: [PATCH 301/375] Fix wrong conflict resolve --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index b45d925c..4a9cf69a 100644 --- a/README.md +++ b/README.md @@ -286,6 +286,7 @@ class RabbitMQJob extends BaseJob return ''; } } +``` ### Use your own Connection From 72d06468e8eb597715c74f4bd976b48a5bcf24f0 Mon Sep 17 00:00:00 2001 From: Emiel Bom Date: Tue, 25 Apr 2023 16:13:04 +0200 Subject: [PATCH 302/375] update --- CHANGELOG-13x.md | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/CHANGELOG-13x.md b/CHANGELOG-13x.md index d7ee1f60..e3a88435 100644 --- a/CHANGELOG-13x.md +++ b/CHANGELOG-13x.md @@ -2,18 +2,24 @@ All notable changes to this project will be documented in this file. -## [unreleased](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v13.2.0...master) +## [unreleased](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v13.3.0...master) ## [13.3.0](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v13.2.0...13.3.0) -- Refactor the creation of RabbitMQ Connection and QueueAPI. [#528](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/528) -- Added configuration object as single dependency for RabbitMQQueue in constructor. [#528](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/528) -- Fix method getExchangeType, not throwing an exception. [#528](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/528) -- Separating the apilogic from the actual publishing to RabbitMQ. [#528](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/528) +- Refactor the creation of RabbitMQ Connection and + QueueAPI. [#528](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/528) +- Added configuration object as single dependency for RabbitMQQueue in + constructor. [#528](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/528) +- Fix method getExchangeType, not throwing an + exception. [#528](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/528) +- Separating the api logic from the actual publishing to + RabbitMQ. [#528](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/528) - Added a reconnect method. [#528](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/528) -- Fix the connection and channel not being fully lazy, when QueueAPI was created. [#528](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/528) +- Fix the connection and channel not being fully lazy, when QueueAPI was + created. [#528](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/528) - Keep track of declared queue's within RabbitMQ. [#528](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/528) - Implemented the 'rest' option to the consumer [#530](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/530) +- Added abbility to reconnect to RabbitMQ, by inserting a Trait into own `RabbitMQQueue:class` [#530](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/530) ## [13.2.0](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v13.1.0...13.2.0) @@ -27,7 +33,8 @@ All notable changes to this project will be documented in this file. ## [13.0.1 (2022-09-16)](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v13.0.0...v13.0.1) -- Add $dispatchAfterCommit when running via Horizon [#484](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/484) +- Add $dispatchAfterCommit when running via + Horizon [#484](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/484) ## [13.0.0 (2022-09-15)](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v12.0.1...v13.0.0) From 0d34ba776ab600499aa535960e9a81df9e046d99 Mon Sep 17 00:00:00 2001 From: Emiel Bom Date: Tue, 25 Apr 2023 18:00:50 +0200 Subject: [PATCH 303/375] rework --- README.md | 124 +++++++++++++++++++------ src/Octane/RabbitMQQueue.php | 11 --- src/Queue/QueueFactory.php | 5 - src/Queue/ReconnectTrait.php | 51 ---------- tests/Feature/QueueTest.php | 6 +- tests/Functional/RabbitMQQueueTest.php | 5 - tests/Functional/TestCase.php | 13 --- tests/TestCase.php | 20 ---- 8 files changed, 102 insertions(+), 133 deletions(-) delete mode 100644 src/Octane/RabbitMQQueue.php delete mode 100644 src/Queue/ReconnectTrait.php diff --git a/README.md b/README.md index e105dd28..99d21d11 100644 --- a/README.md +++ b/README.md @@ -174,30 +174,6 @@ This Library supports Horizon, but in the config you have to inform Laravel to u ], ``` -### Octane support - -Starting with 13.3.0, this package supports [Laravel Octane](https://laravel.com/docs/octane) out of the box. Firstly, -install -Octane and then set `RABBITMQ_WORKER` to `octane`. - -This Library supports Octane, but in the config you have to inform Laravel to use the QueueApi compatible with octane. - -> Note: don't forget to warm 'rabbitmq' connection in the octane config. -```php -'connections' => [ - // ... - - 'rabbitmq' => [ - // ... - - /* Set to "octane" if you wish to use Laravel Octane. */ - 'worker' => env('RABBITMQ_WORKER', 'default'), - ], - - // ... -], -``` - ### Use your own RabbitMQJob class Sometimes you have to work with messages published by another application. @@ -288,7 +264,9 @@ class RabbitMQJob extends BaseJob } ``` -If you want to handle raw message, not in JSON format or without 'job' key in JSON, you should add stub for `getName` method: +If you want to handle raw message, not in JSON format or without 'job' key in JSON, you should add stub for `getName` +method: + ```php Note: Worker classes **must** extend ``VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue`` + +```php +'connections' => [ + // ... + + 'rabbitmq' => [ + // ... + + /* Set to a class if you wish to use your own. */ + 'worker' => \App\Queue\RabbitMQQueue::class, + ], + + // ... +], +``` + +```php + Note: this is not best practice, it is an example. + +```php +reconnect(); + parent::publishBasic($msg, $exchange, $destination, $mandatory, $immediate, $ticket); + } + } + + protected function publishBatch($jobs, $data = '', $queue = null): void + { + try { + parent::publishBatch($jobs, $data, $queue); + } catch (AMQPConnectionClosedException|AMQPChannelClosedException) { + $this->reconnect(); + parent::publishBatch($jobs, $data, $queue); + } + } + + protected function createChannel(): AMQPChannel + { + try { + return parent::createChannel(); + } catch (AMQPConnectionClosedException) { + $this->reconnect(); + return parent::createChannel(); + } + } +} +``` + ### Default Queue The connection does use a default queue with value 'default', when no queue is provided by laravel. @@ -445,10 +510,15 @@ If for some reason you don't want the connection lazy you can turn it off by set ], ``` +### Octane support + +Starting with 13.3.0, this package supports [Laravel Octane](https://laravel.com/docs/octane) out of the box. +Firstly, install Octane and don't forget to warm 'rabbitmq' connection in the octane config. + ## Laravel Usage -Once you completed the configuration you can use the Laravel Queue API. If you used other queue drivers you do not need to -change anything else. If you do not know how to use the Queue API, please refer to the official Laravel +Once you completed the configuration you can use the Laravel Queue API. If you used other queue drivers you do not +need to change anything else. If you do not know how to use the Queue API, please refer to the official Laravel documentation: http://laravel.com/docs/queues ## Lumen Usage diff --git a/src/Octane/RabbitMQQueue.php b/src/Octane/RabbitMQQueue.php deleted file mode 100644 index 9e749efa..00000000 --- a/src/Octane/RabbitMQQueue.php +++ /dev/null @@ -1,11 +0,0 @@ -reconnect(); - parent::publishBasic($msg, $exchange, $destination, $mandatory, $immediate, $ticket); - } - } - - /** - * @throws Exception - */ - protected function publishBatch($jobs, $data = '', $queue = null): void - { - try { - parent::publishBatch($jobs, $data, $queue); - } catch (AMQPConnectionClosedException|AMQPChannelClosedException) { - $this->reconnect(); - parent::publishBatch($jobs, $data, $queue); - } - } - - /** - * @throws Exception - */ - protected function createChannel(): AMQPChannel - { - try { - return parent::createChannel(); - } catch (AMQPConnectionClosedException) { - $this->reconnect(); - - return parent::createChannel(); - } - } -} diff --git a/tests/Feature/QueueTest.php b/tests/Feature/QueueTest.php index 0f8681fc..6418dc51 100644 --- a/tests/Feature/QueueTest.php +++ b/tests/Feature/QueueTest.php @@ -14,7 +14,9 @@ public function setUp(): void { parent::setUp(); - $this->withoutExceptionHandling([AMQPChannelClosedException::class, AMQPConnectionClosedException::class, AMQPProtocolChannelException::class]); + $this->withoutExceptionHandling([ + AMQPChannelClosedException::class, AMQPConnectionClosedException::class, AMQPProtocolChannelException::class, + ]); } public function testConnection(): void @@ -40,6 +42,8 @@ public function testWithoutReconnect(): void public function testReconnect(): void { + $this->markTestSkipped(); + $queue = $this->connection('octane'); $queue->push(new TestJob()); diff --git a/tests/Functional/RabbitMQQueueTest.php b/tests/Functional/RabbitMQQueueTest.php index f68be064..7a106a08 100644 --- a/tests/Functional/RabbitMQQueueTest.php +++ b/tests/Functional/RabbitMQQueueTest.php @@ -4,7 +4,6 @@ use Illuminate\Support\Str; use PhpAmqpLib\Exchange\AMQPExchangeType; -use VladimirYuldashev\LaravelQueueRabbitMQ\Octane\RabbitMQQueue as OctaneRabbitMQQueue; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue; use VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Functional\TestCase as BaseTestCase; @@ -20,10 +19,6 @@ public function testConnection(): void $queue = $this->connection('rabbitmq-with-options-empty'); $this->assertInstanceOf(RabbitMQQueue::class, $queue); - - $queue = $this->connection('rabbitmq-for-octane'); - $this->assertInstanceOf(RabbitMQQueue::class, $queue); - $this->assertInstanceOf(OctaneRabbitMQQueue::class, $queue); } public function testConfigRerouteFailed(): void diff --git a/tests/Functional/TestCase.php b/tests/Functional/TestCase.php index 3a5b56c3..0e0f747d 100644 --- a/tests/Functional/TestCase.php +++ b/tests/Functional/TestCase.php @@ -197,19 +197,6 @@ protected function getEnvironmentSetUp($app): void 'worker' => 'default', ]); - $app['config']->set('queue.connections.rabbitmq-for-octane', [ - 'driver' => 'rabbitmq', - 'hosts' => [ - [ - 'host' => getenv('HOST'), - 'port' => getenv('PORT'), - 'vhost' => '/', - 'user' => 'guest', - 'password' => 'guest', - ], - ], - 'worker' => 'octane', - ]); } /** diff --git a/tests/TestCase.php b/tests/TestCase.php index 9bea59b3..7d50fa67 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -48,26 +48,6 @@ protected function getEnvironmentSetUp($app): void 'worker' => 'default', ]); - $app['config']->set('queue.connections.octane', [ - 'driver' => 'rabbitmq', - 'queue' => 'default', - 'connection' => AMQPLazyConnection::class, - - 'hosts' => [ - [ - 'host' => getenv('HOST'), - 'port' => getenv('PORT'), - 'vhost' => '/', - 'user' => 'guest', - 'password' => 'guest', - ], - ], - - 'options' => [], - - 'worker' => 'octane', - - ]); } protected function connection(string $name = null): RabbitMQQueue From dd6ad6043f5368a78b0c560187246fa26ba477d9 Mon Sep 17 00:00:00 2001 From: Emiel Bom Date: Tue, 25 Apr 2023 18:09:54 +0200 Subject: [PATCH 304/375] rework 2 --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 99d21d11..ede88702 100644 --- a/README.md +++ b/README.md @@ -151,8 +151,7 @@ by adding extra options. ### Horizon support Starting with 8.0, this package supports [Laravel Horizon](https://laravel.com/docs/horizon) out of the box. Firstly, -install -Horizon and then set `RABBITMQ_WORKER` to `horizon`. +install Horizon and then set `RABBITMQ_WORKER` to `horizon`. Horizon is depending on events dispatched by the worker. These events inform Horizon what was done with the message/job. @@ -320,7 +319,7 @@ If you want to use your own `RabbitMQQueue::class` this is possible by extending `VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue`. and inform laravel to use your class by setting `RABBITMQ_WORKER` to `\App\Queue\RabbitMQQueue::class`. -> Note: Worker classes **must** extend ``VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue`` +> Note: Worker classes **must** extend `VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue` ```php 'connections' => [ From ee24fb1de42d8e4015bc28e5fa740cacaac4eb3b Mon Sep 17 00:00:00 2001 From: Emiel Bom Date: Tue, 25 Apr 2023 18:00:50 +0200 Subject: [PATCH 305/375] rework - removed octane shizzle --- README.md | 127 +++++++++++++++++++------ src/Octane/RabbitMQQueue.php | 11 --- src/Queue/QueueFactory.php | 5 - src/Queue/ReconnectTrait.php | 51 ---------- tests/Feature/QueueTest.php | 6 +- tests/Functional/RabbitMQQueueTest.php | 5 - tests/Functional/TestCase.php | 13 --- tests/TestCase.php | 20 ---- 8 files changed, 103 insertions(+), 135 deletions(-) delete mode 100644 src/Octane/RabbitMQQueue.php delete mode 100644 src/Queue/ReconnectTrait.php diff --git a/README.md b/README.md index e105dd28..abaf3518 100644 --- a/README.md +++ b/README.md @@ -151,8 +151,7 @@ by adding extra options. ### Horizon support Starting with 8.0, this package supports [Laravel Horizon](https://laravel.com/docs/horizon) out of the box. Firstly, -install -Horizon and then set `RABBITMQ_WORKER` to `horizon`. +install Horizon and then set `RABBITMQ_WORKER` to `horizon`. Horizon is depending on events dispatched by the worker. These events inform Horizon what was done with the message/job. @@ -174,30 +173,6 @@ This Library supports Horizon, but in the config you have to inform Laravel to u ], ``` -### Octane support - -Starting with 13.3.0, this package supports [Laravel Octane](https://laravel.com/docs/octane) out of the box. Firstly, -install -Octane and then set `RABBITMQ_WORKER` to `octane`. - -This Library supports Octane, but in the config you have to inform Laravel to use the QueueApi compatible with octane. - -> Note: don't forget to warm 'rabbitmq' connection in the octane config. -```php -'connections' => [ - // ... - - 'rabbitmq' => [ - // ... - - /* Set to "octane" if you wish to use Laravel Octane. */ - 'worker' => env('RABBITMQ_WORKER', 'default'), - ], - - // ... -], -``` - ### Use your own RabbitMQJob class Sometimes you have to work with messages published by another application. @@ -288,7 +263,9 @@ class RabbitMQJob extends BaseJob } ``` -If you want to handle raw message, not in JSON format or without 'job' key in JSON, you should add stub for `getName` method: +If you want to handle raw message, not in JSON format or without 'job' key in JSON, +you should add stub for `getName` method: + ```php Note: Worker classes **must** extend `VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue` + +```php +'connections' => [ + // ... + + 'rabbitmq' => [ + // ... + + /* Set to a class if you wish to use your own. */ + 'worker' => \App\Queue\RabbitMQQueue::class, + ], + + // ... +], +``` + +```php + Note: this is not best practice, it is an example. + +```php +reconnect(); + parent::publishBasic($msg, $exchange, $destination, $mandatory, $immediate, $ticket); + } + } + + protected function publishBatch($jobs, $data = '', $queue = null): void + { + try { + parent::publishBatch($jobs, $data, $queue); + } catch (AMQPConnectionClosedException|AMQPChannelClosedException) { + $this->reconnect(); + parent::publishBatch($jobs, $data, $queue); + } + } + + protected function createChannel(): AMQPChannel + { + try { + return parent::createChannel(); + } catch (AMQPConnectionClosedException) { + $this->reconnect(); + return parent::createChannel(); + } + } +} +``` + ### Default Queue The connection does use a default queue with value 'default', when no queue is provided by laravel. @@ -445,10 +509,15 @@ If for some reason you don't want the connection lazy you can turn it off by set ], ``` +### Octane support + +Starting with 13.3.0, this package supports [Laravel Octane](https://laravel.com/docs/octane) out of the box. +Firstly, install Octane and don't forget to warm 'rabbitmq' connection in the octane config. + ## Laravel Usage -Once you completed the configuration you can use the Laravel Queue API. If you used other queue drivers you do not need to -change anything else. If you do not know how to use the Queue API, please refer to the official Laravel +Once you completed the configuration you can use the Laravel Queue API. If you used other queue drivers you do not +need to change anything else. If you do not know how to use the Queue API, please refer to the official Laravel documentation: http://laravel.com/docs/queues ## Lumen Usage diff --git a/src/Octane/RabbitMQQueue.php b/src/Octane/RabbitMQQueue.php deleted file mode 100644 index 9e749efa..00000000 --- a/src/Octane/RabbitMQQueue.php +++ /dev/null @@ -1,11 +0,0 @@ -reconnect(); - parent::publishBasic($msg, $exchange, $destination, $mandatory, $immediate, $ticket); - } - } - - /** - * @throws Exception - */ - protected function publishBatch($jobs, $data = '', $queue = null): void - { - try { - parent::publishBatch($jobs, $data, $queue); - } catch (AMQPConnectionClosedException|AMQPChannelClosedException) { - $this->reconnect(); - parent::publishBatch($jobs, $data, $queue); - } - } - - /** - * @throws Exception - */ - protected function createChannel(): AMQPChannel - { - try { - return parent::createChannel(); - } catch (AMQPConnectionClosedException) { - $this->reconnect(); - - return parent::createChannel(); - } - } -} diff --git a/tests/Feature/QueueTest.php b/tests/Feature/QueueTest.php index 0f8681fc..6418dc51 100644 --- a/tests/Feature/QueueTest.php +++ b/tests/Feature/QueueTest.php @@ -14,7 +14,9 @@ public function setUp(): void { parent::setUp(); - $this->withoutExceptionHandling([AMQPChannelClosedException::class, AMQPConnectionClosedException::class, AMQPProtocolChannelException::class]); + $this->withoutExceptionHandling([ + AMQPChannelClosedException::class, AMQPConnectionClosedException::class, AMQPProtocolChannelException::class, + ]); } public function testConnection(): void @@ -40,6 +42,8 @@ public function testWithoutReconnect(): void public function testReconnect(): void { + $this->markTestSkipped(); + $queue = $this->connection('octane'); $queue->push(new TestJob()); diff --git a/tests/Functional/RabbitMQQueueTest.php b/tests/Functional/RabbitMQQueueTest.php index f68be064..7a106a08 100644 --- a/tests/Functional/RabbitMQQueueTest.php +++ b/tests/Functional/RabbitMQQueueTest.php @@ -4,7 +4,6 @@ use Illuminate\Support\Str; use PhpAmqpLib\Exchange\AMQPExchangeType; -use VladimirYuldashev\LaravelQueueRabbitMQ\Octane\RabbitMQQueue as OctaneRabbitMQQueue; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue; use VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Functional\TestCase as BaseTestCase; @@ -20,10 +19,6 @@ public function testConnection(): void $queue = $this->connection('rabbitmq-with-options-empty'); $this->assertInstanceOf(RabbitMQQueue::class, $queue); - - $queue = $this->connection('rabbitmq-for-octane'); - $this->assertInstanceOf(RabbitMQQueue::class, $queue); - $this->assertInstanceOf(OctaneRabbitMQQueue::class, $queue); } public function testConfigRerouteFailed(): void diff --git a/tests/Functional/TestCase.php b/tests/Functional/TestCase.php index 3a5b56c3..0e0f747d 100644 --- a/tests/Functional/TestCase.php +++ b/tests/Functional/TestCase.php @@ -197,19 +197,6 @@ protected function getEnvironmentSetUp($app): void 'worker' => 'default', ]); - $app['config']->set('queue.connections.rabbitmq-for-octane', [ - 'driver' => 'rabbitmq', - 'hosts' => [ - [ - 'host' => getenv('HOST'), - 'port' => getenv('PORT'), - 'vhost' => '/', - 'user' => 'guest', - 'password' => 'guest', - ], - ], - 'worker' => 'octane', - ]); } /** diff --git a/tests/TestCase.php b/tests/TestCase.php index 9bea59b3..7d50fa67 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -48,26 +48,6 @@ protected function getEnvironmentSetUp($app): void 'worker' => 'default', ]); - $app['config']->set('queue.connections.octane', [ - 'driver' => 'rabbitmq', - 'queue' => 'default', - 'connection' => AMQPLazyConnection::class, - - 'hosts' => [ - [ - 'host' => getenv('HOST'), - 'port' => getenv('PORT'), - 'vhost' => '/', - 'user' => 'guest', - 'password' => 'guest', - ], - ], - - 'options' => [], - - 'worker' => 'octane', - - ]); } protected function connection(string $name = null): RabbitMQQueue From 0c0b396e2a5593e2089ba280ab3217d82ca0daee Mon Sep 17 00:00:00 2001 From: Emiel Bom Date: Tue, 25 Apr 2023 18:29:04 +0200 Subject: [PATCH 306/375] update --- CHANGELOG-13x.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG-13x.md b/CHANGELOG-13x.md index e3a88435..88da7e2f 100644 --- a/CHANGELOG-13x.md +++ b/CHANGELOG-13x.md @@ -19,7 +19,8 @@ All notable changes to this project will be documented in this file. created. [#528](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/528) - Keep track of declared queue's within RabbitMQ. [#528](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/528) - Implemented the 'rest' option to the consumer [#530](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/530) -- Added abbility to reconnect to RabbitMQ, by inserting a Trait into own `RabbitMQQueue:class` [#530](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/530) +- Added ability to reconnect to RabbitMQ, by creating your + own `RabbitMQQueue:class` [#531](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/531) ## [13.2.0](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v13.1.0...13.2.0) From 86a1594e9be98ffa03fd722e7298ef797bf9205e Mon Sep 17 00:00:00 2001 From: Emiel Bom Date: Wed, 26 Apr 2023 10:44:34 +0200 Subject: [PATCH 307/375] update: warm rabbitmq connection reference --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index abaf3518..edd8d564 100644 --- a/README.md +++ b/README.md @@ -513,6 +513,7 @@ If for some reason you don't want the connection lazy you can turn it off by set Starting with 13.3.0, this package supports [Laravel Octane](https://laravel.com/docs/octane) out of the box. Firstly, install Octane and don't forget to warm 'rabbitmq' connection in the octane config. +> See: https://github.com/vyuldashev/laravel-queue-rabbitmq/issues/460#issuecomment-1469851667 ## Laravel Usage From 826ed957c5fb00790f49d60fbcd53ecd28805775 Mon Sep 17 00:00:00 2001 From: Emiel Bom Date: Wed, 26 Apr 2023 14:31:52 +0200 Subject: [PATCH 308/375] update: incorrect use statements in examples --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index edd8d564..8f0081f1 100644 --- a/README.md +++ b/README.md @@ -263,7 +263,7 @@ class RabbitMQJob extends BaseJob } ``` -If you want to handle raw message, not in JSON format or without 'job' key in JSON, +If you want to handle raw message, not in JSON format or without 'job' key in JSON, you should add stub for `getName` method: ```php @@ -342,7 +342,6 @@ and inform laravel to use your class by setting `RABBITMQ_WORKER` to `\App\Queue namespace App\Queue; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue as BaseRabbitMQQueue; -use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\ReconnectTrait; class RabbitMQQueue extends BaseRabbitMQQueue { @@ -362,8 +361,9 @@ You can override the publishing and the createChannel methods. namespace App\Queue; +use PhpAmqpLib\Exception\AMQPChannelClosedException; +use PhpAmqpLib\Exception\AMQPConnectionClosedException; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue as BaseRabbitMQQueue; -use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\ReconnectTrait; class RabbitMQQueue extends BaseRabbitMQQueue { From e67d4fc925e5c5d9bdd1b891743181018c9d8218 Mon Sep 17 00:00:00 2001 From: Emiel Bom Date: Wed, 26 Apr 2023 17:02:19 +0200 Subject: [PATCH 309/375] update: reconnect tests --- tests/Feature/QueueTest.php | 23 ++--------------------- tests/Functional/TestCase.php | 31 +++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 21 deletions(-) diff --git a/tests/Feature/QueueTest.php b/tests/Feature/QueueTest.php index 6418dc51..5ecfb978 100644 --- a/tests/Feature/QueueTest.php +++ b/tests/Feature/QueueTest.php @@ -15,7 +15,8 @@ public function setUp(): void parent::setUp(); $this->withoutExceptionHandling([ - AMQPChannelClosedException::class, AMQPConnectionClosedException::class, AMQPProtocolChannelException::class, + AMQPChannelClosedException::class, AMQPConnectionClosedException::class, + AMQPProtocolChannelException::class, ]); } @@ -39,24 +40,4 @@ public function testWithoutReconnect(): void $this->expectException(AMQPChannelClosedException::class); $queue->push(new TestJob()); } - - public function testReconnect(): void - { - $this->markTestSkipped(); - - $queue = $this->connection('octane'); - - $queue->push(new TestJob()); - sleep(1); - $this->assertSame(1, $queue->size()); - - // close connection - $queue->getConnection()->close(); - $this->assertFalse($queue->getConnection()->isConnected()); - - $queue->push(new TestJob()); - sleep(1); - $this->assertTrue($queue->getConnection()->isConnected()); - $this->assertSame(2, $queue->size()); - } } diff --git a/tests/Functional/TestCase.php b/tests/Functional/TestCase.php index 0e0f747d..a6b7e38f 100644 --- a/tests/Functional/TestCase.php +++ b/tests/Functional/TestCase.php @@ -3,6 +3,7 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Functional; use Exception; +use PhpAmqpLib\Channel\AMQPChannel; use ReflectionClass; use ReflectionException; use VladimirYuldashev\LaravelQueueRabbitMQ\Tests\TestCase as BaseTestCase; @@ -234,4 +235,34 @@ protected function callProperty($object, string $property): mixed return $property->getValue($object); } + + public function testConnectChannel(): void + { + $queue = $this->connection(); + $this->assertFalse($queue->getConnection()->isConnected()); + + /** @var AMQPChannel $channel */ + $channel = $this->callMethod($queue, 'getChannel'); + $this->assertTrue($queue->getConnection()->isConnected()); + $this->assertSame($channel, $this->callProperty($queue, 'channel')); + $this->assertTrue($channel->is_open()); + } + + public function testReconnect(): void + { + $queue = $this->connection(); + $this->assertFalse($queue->getConnection()->isConnected()); + + // connect + $channel = $this->callMethod($queue, 'getChannel'); + $this->assertTrue($queue->getConnection()->isConnected()); + $this->assertSame($channel, $this->callProperty($queue, 'channel')); + + // reconnect + $queue->getConnection()->close(); + $this->assertFalse($queue->getConnection()->isConnected()); + $this->callMethod($queue, 'reconnect'); + $this->assertTrue($queue->getConnection()->isConnected()); + $this->assertTrue($queue->getChannel()->is_open()); + } } From c1e6d2d177e110e5f3d54a9a851e94df624d089b Mon Sep 17 00:00:00 2001 From: Emiel Bom Date: Wed, 26 Apr 2023 17:07:24 +0200 Subject: [PATCH 310/375] update: reconnect tests --- tests/Functional/TestCase.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/Functional/TestCase.php b/tests/Functional/TestCase.php index a6b7e38f..8b843561 100644 --- a/tests/Functional/TestCase.php +++ b/tests/Functional/TestCase.php @@ -258,9 +258,11 @@ public function testReconnect(): void $this->assertTrue($queue->getConnection()->isConnected()); $this->assertSame($channel, $this->callProperty($queue, 'channel')); - // reconnect + // close $queue->getConnection()->close(); $this->assertFalse($queue->getConnection()->isConnected()); + + // reconnect $this->callMethod($queue, 'reconnect'); $this->assertTrue($queue->getConnection()->isConnected()); $this->assertTrue($queue->getChannel()->is_open()); From 1b9ff657a6d5cb265837c6c8f9c8a621a0ef7765 Mon Sep 17 00:00:00 2001 From: Oleksii Prudkyi Date: Tue, 30 May 2023 07:58:20 +0300 Subject: [PATCH 311/375] fix: crash on accessing $currentJob before initialization --- src/Queue/RabbitMQQueue.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index 57c23e64..96a803fe 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -577,7 +577,7 @@ protected function getRandomId(): string */ public function close(): void { - if (! $this->currentJob->isDeletedOrReleased()) { + if (isset($this->currentJob) && ! $this->currentJob->isDeletedOrReleased()) { $this->reject($this->currentJob, true); } From c8ca2170807e302e19b06562cee57eac493b90b7 Mon Sep 17 00:00:00 2001 From: Sebastien Armand Date: Thu, 1 Jun 2023 16:29:00 -0700 Subject: [PATCH 312/375] update changelog --- CHANGELOG-13x.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG-13x.md b/CHANGELOG-13x.md index 88da7e2f..64e448ce 100644 --- a/CHANGELOG-13x.md +++ b/CHANGELOG-13x.md @@ -4,6 +4,9 @@ All notable changes to this project will be documented in this file. ## [unreleased](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v13.3.0...master) +## [13.3.1](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v13.3.0...13.3.3) +- Fix a bug when no job / message is available on the queue initially [#543](https://github.com/vyuldashev/laravel-queue-rabbitmq/pull/543) + ## [13.3.0](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v13.2.0...13.3.0) - Refactor the creation of RabbitMQ Connection and From 9051e4716d5aaea2be33a0ce745e5c2b3b7e74ac Mon Sep 17 00:00:00 2001 From: Emiel Bom <11626777+adm-bome@users.noreply.github.com> Date: Wed, 14 Jun 2023 00:50:43 +0200 Subject: [PATCH 313/375] Switched the order of the parameters --- src/Queue/RabbitMQQueue.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index 96a803fe..cf52eb80 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -183,7 +183,7 @@ public function laterRaw($delay, string $payload, $queue = null, int $attempts = */ public function bulk($jobs, $data = '', $queue = null): void { - $this->publishBatch($jobs, $queue, $data); + $this->publishBatch($jobs, $data, $queue); } /** From f864aa8e128df16e1aab45d76fdd6328ca6c6231 Mon Sep 17 00:00:00 2001 From: Maxime Tassy Date: Fri, 7 Jul 2023 16:43:58 +0200 Subject: [PATCH 314/375] Fix #553, add possibility to define network protocol in config --- README.md | 23 +++++++++++++++++++++++ src/Queue/Connection/ConfigFactory.php | 8 ++++++++ 2 files changed, 31 insertions(+) diff --git a/README.md b/README.md index 8f0081f1..532a2634 100644 --- a/README.md +++ b/README.md @@ -509,6 +509,29 @@ If for some reason you don't want the connection lazy you can turn it off by set ], ``` +### Network Protocol + +By default, the network protocol used for connection is tcp. +If for some reason you want to use another network protocol, you can add the extra value in your config options. + +```php +'connections' => [ + // ... + + 'rabbitmq' => [ + // ... + + 'options' => [ + // ... + + 'network_protocol' => 'network wanted', + ], + ], + + // ... +], +``` + ### Octane support Starting with 13.3.0, this package supports [Laravel Octane](https://laravel.com/docs/octane) out of the box. diff --git a/src/Queue/Connection/ConfigFactory.php b/src/Queue/Connection/ConfigFactory.php index c6e24699..b8dc1bd5 100644 --- a/src/Queue/Connection/ConfigFactory.php +++ b/src/Queue/Connection/ConfigFactory.php @@ -37,6 +37,7 @@ public static function make(array $config = []): AMQPConnectionConfig self::getHostFromConfig($connectionConfig, $config); self::getHeartbeatFromConfig($connectionConfig, $config); + self::getNetworkProtocolFromConfig($connectionConfig, $config); }); } @@ -90,4 +91,11 @@ protected static function getHeartbeatFromConfig(AMQPConnectionConfig $connectio $connectionConfig->setHeartbeat((int) $heartbeat); } } + + protected static function getNetworkProtocolFromConfig(AMQPConnectionConfig $connectionConfig, array $config): void + { + if ($networkProtocol = Arr::get($config, self::CONFIG_OPTIONS.'.network_protocol')) { + $connectionConfig->setNetworkProtocol($networkProtocol); + } + } } From 2129c44c6313fa7a974ca6df240bf7b51cd607a9 Mon Sep 17 00:00:00 2001 From: Seb Date: Tue, 11 Jul 2023 10:47:41 -0700 Subject: [PATCH 315/375] Fix issue where was accessed before initialization --- src/Horizon/RabbitMQQueue.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Horizon/RabbitMQQueue.php b/src/Horizon/RabbitMQQueue.php index e4266a76..894cc24b 100644 --- a/src/Horizon/RabbitMQQueue.php +++ b/src/Horizon/RabbitMQQueue.php @@ -48,7 +48,7 @@ public function push($job, $data = '', $queue = null) */ public function pushRaw($payload, $queue = null, array $options = []): int|string|null { - $payload = (new JobPayload($payload))->prepare($this->lastPushed)->value; + $payload = (new JobPayload($payload))->prepare($this->lastPushed ?? null)->value; return tap(parent::pushRaw($payload, $queue, $options), function () use ($queue, $payload): void { $this->event($this->getQueue($queue), new JobPushed($payload)); From 1da950c93ed30b93ee0477f128a5afbdc35706b4 Mon Sep 17 00:00:00 2001 From: Seb Date: Tue, 11 Jul 2023 10:57:38 -0700 Subject: [PATCH 316/375] like this then --- src/Horizon/RabbitMQQueue.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Horizon/RabbitMQQueue.php b/src/Horizon/RabbitMQQueue.php index 894cc24b..fbd712b4 100644 --- a/src/Horizon/RabbitMQQueue.php +++ b/src/Horizon/RabbitMQQueue.php @@ -18,7 +18,7 @@ class RabbitMQQueue extends BaseRabbitMQQueue /** * The job that last pushed to queue via the "push" method. */ - protected string|object $lastPushed; + protected string|object|null $lastPushed = null; /** * Get the number of queue jobs that are ready to process. @@ -48,7 +48,7 @@ public function push($job, $data = '', $queue = null) */ public function pushRaw($payload, $queue = null, array $options = []): int|string|null { - $payload = (new JobPayload($payload))->prepare($this->lastPushed ?? null)->value; + $payload = (new JobPayload($payload))->prepare($this->lastPushed)->value; return tap(parent::pushRaw($payload, $queue, $options), function () use ($queue, $payload): void { $this->event($this->getQueue($queue), new JobPushed($payload)); From df1b3c3d902bcc631297e8d2b48e39dafe3419aa Mon Sep 17 00:00:00 2001 From: Seb Date: Tue, 11 Jul 2023 11:00:37 -0700 Subject: [PATCH 317/375] like this then --- src/Horizon/RabbitMQQueue.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Horizon/RabbitMQQueue.php b/src/Horizon/RabbitMQQueue.php index fbd712b4..924e4649 100644 --- a/src/Horizon/RabbitMQQueue.php +++ b/src/Horizon/RabbitMQQueue.php @@ -18,7 +18,7 @@ class RabbitMQQueue extends BaseRabbitMQQueue /** * The job that last pushed to queue via the "push" method. */ - protected string|object|null $lastPushed = null; + protected string|object $lastPushed = ''; /** * Get the number of queue jobs that are ready to process. From c63c217202871d5e80973d2c75406661fc543a62 Mon Sep 17 00:00:00 2001 From: Seb Date: Tue, 11 Jul 2023 11:16:42 -0700 Subject: [PATCH 318/375] ending the battle with pint --- src/Horizon/RabbitMQQueue.php | 4 ++-- src/Queue/RabbitMQQueue.php | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Horizon/RabbitMQQueue.php b/src/Horizon/RabbitMQQueue.php index 924e4649..894cc24b 100644 --- a/src/Horizon/RabbitMQQueue.php +++ b/src/Horizon/RabbitMQQueue.php @@ -18,7 +18,7 @@ class RabbitMQQueue extends BaseRabbitMQQueue /** * The job that last pushed to queue via the "push" method. */ - protected string|object $lastPushed = ''; + protected string|object $lastPushed; /** * Get the number of queue jobs that are ready to process. @@ -48,7 +48,7 @@ public function push($job, $data = '', $queue = null) */ public function pushRaw($payload, $queue = null, array $options = []): int|string|null { - $payload = (new JobPayload($payload))->prepare($this->lastPushed)->value; + $payload = (new JobPayload($payload))->prepare($this->lastPushed ?? null)->value; return tap(parent::pushRaw($payload, $queue, $options), function () use ($queue, $payload): void { $this->event($this->getQueue($queue), new JobPushed($payload)); diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index cf52eb80..f477f7c5 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -631,7 +631,7 @@ protected function getDelayQueueArguments(string $destination, int $ttl): array /** * Get the exchange name, or empty string; as default value. */ - protected function getExchange(?string $exchange = null): string + protected function getExchange(string $exchange = null): string { return $exchange ?? $this->getConfig()->getExchange(); } @@ -648,7 +648,7 @@ protected function getRoutingKey(string $destination): string /** * Get the exchangeType, or AMQPExchangeType::DIRECT as default. */ - protected function getExchangeType(?string $type = null): string + protected function getExchangeType(string $type = null): string { $constant = AMQPExchangeType::class.'::'.Str::upper($type ?: $this->getConfig()->getExchangeType()); @@ -658,7 +658,7 @@ protected function getExchangeType(?string $type = null): string /** * Get the exchange for failed messages. */ - protected function getFailedExchange(?string $exchange = null): string + protected function getFailedExchange(string $exchange = null): string { return $exchange ?? $this->getConfig()->getFailedExchange(); } @@ -693,7 +693,7 @@ protected function isQueueDeclared(string $name): bool * * @throws AMQPProtocolChannelException */ - protected function declareDestination(string $destination, ?string $exchange = null, string $exchangeType = AMQPExchangeType::DIRECT): void + protected function declareDestination(string $destination, string $exchange = null, string $exchangeType = AMQPExchangeType::DIRECT): void { // When an exchange is provided and no exchange is present in RabbitMQ, create an exchange. if ($exchange && ! $this->isExchangeExists($exchange)) { From 2aeeb04db1cb1c3ec051898b2a1b572248cc6c2d Mon Sep 17 00:00:00 2001 From: Maxime Tassy Date: Wed, 12 Jul 2023 09:05:40 +0200 Subject: [PATCH 319/375] Move network_protocol up in the config object and document available protocols --- README.md | 7 ++----- src/Queue/Connection/ConfigFactory.php | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 532a2634..149e7e9c 100644 --- a/README.md +++ b/README.md @@ -513,6 +513,7 @@ If for some reason you don't want the connection lazy you can turn it off by set By default, the network protocol used for connection is tcp. If for some reason you want to use another network protocol, you can add the extra value in your config options. +Available protocols : `tcp`, `ssl`, `tls` ```php 'connections' => [ @@ -521,11 +522,7 @@ If for some reason you want to use another network protocol, you can add the ext 'rabbitmq' => [ // ... - 'options' => [ - // ... - - 'network_protocol' => 'network wanted', - ], + 'network_protocol' => 'tcp', ], // ... diff --git a/src/Queue/Connection/ConfigFactory.php b/src/Queue/Connection/ConfigFactory.php index b8dc1bd5..859a7850 100644 --- a/src/Queue/Connection/ConfigFactory.php +++ b/src/Queue/Connection/ConfigFactory.php @@ -94,7 +94,7 @@ protected static function getHeartbeatFromConfig(AMQPConnectionConfig $connectio protected static function getNetworkProtocolFromConfig(AMQPConnectionConfig $connectionConfig, array $config): void { - if ($networkProtocol = Arr::get($config, self::CONFIG_OPTIONS.'.network_protocol')) { + if ($networkProtocol = Arr::get($config, 'network_protocol')) { $connectionConfig->setNetworkProtocol($networkProtocol); } } From 2542290a11e89d982b8825c9114da009b7bd2c07 Mon Sep 17 00:00:00 2001 From: Seb Date: Fri, 14 Jul 2023 10:07:42 -0700 Subject: [PATCH 320/375] Allow SSL Connections with no peer verification --- src/Queue/Connection/ConfigFactory.php | 3 +- src/Queue/Connection/ConnectionFactory.php | 6 +-- tests/Feature/ConnectorTest.php | 45 ++++++++++++++++++++++ tests/Mocks/TestSSLConnection.php | 14 +++++++ 4 files changed, 64 insertions(+), 4 deletions(-) create mode 100644 tests/Mocks/TestSSLConnection.php diff --git a/src/Queue/Connection/ConfigFactory.php b/src/Queue/Connection/ConfigFactory.php index 859a7850..7783b9cd 100644 --- a/src/Queue/Connection/ConfigFactory.php +++ b/src/Queue/Connection/ConfigFactory.php @@ -75,7 +75,8 @@ protected static function getSLLOptionsFromConfig(AMQPConnectionConfig $connecti if ($key = Arr::get($sslConfig, 'local_key')) { $connectionConfig->setSslKey($key); } - if ($verifyPeer = Arr::get($sslConfig, 'verify_peer')) { + if (Arr::has($sslConfig, 'verify_peer')) { + $verifyPeer = Arr::get($sslConfig, 'verify_peer'); $connectionConfig->setSslVerify($verifyPeer); } if ($passphrase = Arr::get($sslConfig, 'passphrase')) { diff --git a/src/Queue/Connection/ConnectionFactory.php b/src/Queue/Connection/ConnectionFactory.php index df19f223..1d6a46e8 100644 --- a/src/Queue/Connection/ConnectionFactory.php +++ b/src/Queue/Connection/ConnectionFactory.php @@ -200,10 +200,10 @@ protected static function assertSSLConnection($connection): void self::assertExtendedOf($connection, self::CONNECTION_SUB_TYPE_SSL); } - protected static function assertExtendedOf($connection, string $abstract): void + protected static function assertExtendedOf($connection, string $parent): void { - if (! is_subclass_of($connection, $abstract)) { - throw new AMQPLogicException(sprintf('The connection must extend: %s', class_basename($abstract))); + if (! is_subclass_of($connection, $parent) && $connection !== $parent) { + throw new AMQPLogicException(sprintf('The connection must extend: %s', class_basename($parent))); } } diff --git a/tests/Feature/ConnectorTest.php b/tests/Feature/ConnectorTest.php index 3ecede98..d4f62258 100644 --- a/tests/Feature/ConnectorTest.php +++ b/tests/Feature/ConnectorTest.php @@ -3,10 +3,12 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Feature; use Illuminate\Queue\QueueManager; +use PhpAmqpLib\Connection\AMQPConnectionConfig; use PhpAmqpLib\Connection\AMQPLazyConnection; use PhpAmqpLib\Connection\AMQPSSLConnection; use PhpAmqpLib\Connection\AMQPStreamConnection; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue; +use VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Mocks\TestSSLConnection; class ConnectorTest extends \VladimirYuldashev\LaravelQueueRabbitMQ\Tests\TestCase { @@ -138,4 +140,47 @@ public function testSslConnection(): void $this->assertTrue($connection->getConnection()->isConnected()); $this->assertTrue($connection->getChannel()->is_open()); } + + public function testNoVerificationSslConnection(): void + { + $this->app['config']->set('queue.connections.rabbitmq', [ + 'driver' => 'rabbitmq', + 'queue' => env('RABBITMQ_QUEUE', 'default'), + 'connection' => TestSSLConnection::class, + 'secure' => true, + + 'hosts' => [ + [ + 'host' => getenv('HOST'), + 'port' => getenv('PORT_SSL'), + 'user' => 'guest', + 'password' => 'guest', + 'vhost' => '/', + ], + ], + + 'options' => [ + 'ssl_options' => [ + 'cafile' => getenv('RABBITMQ_SSL_CAFILE'), + 'local_cert' => null, + 'local_key' => null, + 'verify_peer' => false, + 'passphrase' => null, + ], + ], + + 'worker' => env('RABBITMQ_WORKER', 'default'), + ]); + + /** @var QueueManager $queue */ + $queue = $this->app['queue']; + + /** @var RabbitMQQueue $connection */ + $connection = $queue->connection('rabbitmq'); + $this->assertInstanceOf(RabbitMQQueue::class, $connection); + $this->assertInstanceOf(AMQPSSLConnection::class, $connection->getConnection()); + /** @var AMQPConnectionConfig */ + $config = $connection->getConnection()->getConfig(); + $this->assertFalse($config->getSslVerify()); + } } diff --git a/tests/Mocks/TestSSLConnection.php b/tests/Mocks/TestSSLConnection.php new file mode 100644 index 00000000..c1586475 --- /dev/null +++ b/tests/Mocks/TestSSLConnection.php @@ -0,0 +1,14 @@ +config; + } +} From 9ae8cd62d247a1598c1a7eff0924c7d8816f9fbe Mon Sep 17 00:00:00 2001 From: Seb Date: Thu, 28 Sep 2023 09:23:02 -0700 Subject: [PATCH 321/375] explain --- tests/Feature/ConnectorTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/Feature/ConnectorTest.php b/tests/Feature/ConnectorTest.php index d4f62258..91660ad2 100644 --- a/tests/Feature/ConnectorTest.php +++ b/tests/Feature/ConnectorTest.php @@ -141,6 +141,7 @@ public function testSslConnection(): void $this->assertTrue($connection->getChannel()->is_open()); } + // Test to validate ssl connection params public function testNoVerificationSslConnection(): void { $this->app['config']->set('queue.connections.rabbitmq', [ From 9e4f32e0b62d38714fe0473adb92d08afc2ac91c Mon Sep 17 00:00:00 2001 From: Seb Date: Thu, 28 Sep 2023 09:26:36 -0700 Subject: [PATCH 322/375] pint --- src/Queue/Connection/ConnectionFactory.php | 2 +- src/Queue/RabbitMQQueue.php | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Queue/Connection/ConnectionFactory.php b/src/Queue/Connection/ConnectionFactory.php index 1d6a46e8..f5ccad0e 100644 --- a/src/Queue/Connection/ConnectionFactory.php +++ b/src/Queue/Connection/ConnectionFactory.php @@ -170,7 +170,7 @@ protected static function getSslOptions(AMQPConnectionConfig $config): array 'ciphers' => $config->getSslCiphers(), 'security_level' => $config->getSslSecurityLevel(), ], static function ($value) { - return null !== $value; + return $value !== null; }); } diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index f477f7c5..0bf2e608 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -303,8 +303,6 @@ public function getJobClass(): string /** * Gets a queue/destination, by default the queue option set on the connection. - * - * @param null $queue */ public function getQueue($queue = null): string { From d6903405cf1bfa52436e9184777f5d6eb4fc32f1 Mon Sep 17 00:00:00 2001 From: Seb Date: Fri, 29 Sep 2023 16:34:47 -0700 Subject: [PATCH 323/375] fix test --- src/Queue/Connection/ConnectionFactory.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Queue/Connection/ConnectionFactory.php b/src/Queue/Connection/ConnectionFactory.php index f5ccad0e..e6641b95 100644 --- a/src/Queue/Connection/ConnectionFactory.php +++ b/src/Queue/Connection/ConnectionFactory.php @@ -159,9 +159,14 @@ protected static function getReadWriteTimeout(AMQPConnectionConfig $config): flo protected static function getSslOptions(AMQPConnectionConfig $config): array { + $path = null; + if (method_exists($config, 'getSslCaPath')) { + $path = $config->getSslCaPath(); + } + return array_filter([ 'cafile' => $config->getSslCaCert(), - 'capath' => $config->getSslCaPath(), + 'capath' => $path, 'local_cert' => $config->getSslCert(), 'local_pk' => $config->getSslKey(), 'verify_peer' => $config->getSslVerify(), From 2b42641f85283bc42c76c8596e16a201f82ab342 Mon Sep 17 00:00:00 2001 From: Seb Date: Mon, 2 Oct 2023 08:33:41 -0700 Subject: [PATCH 324/375] compatibilities --- src/Queue/Connection/ConnectionFactory.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Queue/Connection/ConnectionFactory.php b/src/Queue/Connection/ConnectionFactory.php index e6641b95..97d7c1a6 100644 --- a/src/Queue/Connection/ConnectionFactory.php +++ b/src/Queue/Connection/ConnectionFactory.php @@ -163,6 +163,10 @@ protected static function getSslOptions(AMQPConnectionConfig $config): array if (method_exists($config, 'getSslCaPath')) { $path = $config->getSslCaPath(); } + $securityLevel = null; + if (method_exists($config, 'getSslSecurityLevel')) { + $securityLevel = $config->getSslSecurityLevel(); + } return array_filter([ 'cafile' => $config->getSslCaCert(), @@ -173,7 +177,7 @@ protected static function getSslOptions(AMQPConnectionConfig $config): array 'verify_peer_name' => $config->getSslVerifyName(), 'passphrase' => $config->getSslPassPhrase(), 'ciphers' => $config->getSslCiphers(), - 'security_level' => $config->getSslSecurityLevel(), + 'security_level' => $securityLevel, ], static function ($value) { return $value !== null; }); From 47ef31e7e77a6d1b65b57423890830ba6518da8d Mon Sep 17 00:00:00 2001 From: Seb Date: Mon, 2 Oct 2023 08:43:11 -0700 Subject: [PATCH 325/375] fix --- src/Queue/Connection/ConnectionFactory.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Queue/Connection/ConnectionFactory.php b/src/Queue/Connection/ConnectionFactory.php index 97d7c1a6..00447332 100644 --- a/src/Queue/Connection/ConnectionFactory.php +++ b/src/Queue/Connection/ConnectionFactory.php @@ -91,7 +91,7 @@ protected static function createSocketConnection($connection, AMQPConnectionConf $config->getVhost(), $config->isInsist(), $config->getLoginMethod(), - $config->getLoginResponse(), + $config->getLoginResponse() ?? null, $config->getLocale(), $config->getReadTimeout(), $config->isKeepalive(), From 07cab330f9caa5667464149dcee05c98f2b00cee Mon Sep 17 00:00:00 2001 From: Seb Date: Mon, 2 Oct 2023 08:46:30 -0700 Subject: [PATCH 326/375] compatibility --- composer.json | 2 +- src/Queue/Connection/ConnectionFactory.php | 15 +++------------ 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/composer.json b/composer.json index 20cf5310..2897c08e 100644 --- a/composer.json +++ b/composer.json @@ -12,7 +12,7 @@ "php": "^8.0", "ext-json": "*", "illuminate/queue": "^9.0|^10.0", - "php-amqplib/php-amqplib": "^v3.2" + "php-amqplib/php-amqplib": "^v3.3" }, "require-dev": { "phpunit/phpunit": "^9.3", diff --git a/src/Queue/Connection/ConnectionFactory.php b/src/Queue/Connection/ConnectionFactory.php index 00447332..f5ccad0e 100644 --- a/src/Queue/Connection/ConnectionFactory.php +++ b/src/Queue/Connection/ConnectionFactory.php @@ -91,7 +91,7 @@ protected static function createSocketConnection($connection, AMQPConnectionConf $config->getVhost(), $config->isInsist(), $config->getLoginMethod(), - $config->getLoginResponse() ?? null, + $config->getLoginResponse(), $config->getLocale(), $config->getReadTimeout(), $config->isKeepalive(), @@ -159,25 +159,16 @@ protected static function getReadWriteTimeout(AMQPConnectionConfig $config): flo protected static function getSslOptions(AMQPConnectionConfig $config): array { - $path = null; - if (method_exists($config, 'getSslCaPath')) { - $path = $config->getSslCaPath(); - } - $securityLevel = null; - if (method_exists($config, 'getSslSecurityLevel')) { - $securityLevel = $config->getSslSecurityLevel(); - } - return array_filter([ 'cafile' => $config->getSslCaCert(), - 'capath' => $path, + 'capath' => $config->getSslCaPath(), 'local_cert' => $config->getSslCert(), 'local_pk' => $config->getSslKey(), 'verify_peer' => $config->getSslVerify(), 'verify_peer_name' => $config->getSslVerifyName(), 'passphrase' => $config->getSslPassPhrase(), 'ciphers' => $config->getSslCiphers(), - 'security_level' => $securityLevel, + 'security_level' => $config->getSslSecurityLevel(), ], static function ($value) { return $value !== null; }); From d80c00e9a5e991cf96b979d179ec843476cd8371 Mon Sep 17 00:00:00 2001 From: Seb Date: Mon, 2 Oct 2023 08:51:31 -0700 Subject: [PATCH 327/375] will we get there --- src/Queue/Connection/ConnectionFactory.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Queue/Connection/ConnectionFactory.php b/src/Queue/Connection/ConnectionFactory.php index f5ccad0e..7dd95e99 100644 --- a/src/Queue/Connection/ConnectionFactory.php +++ b/src/Queue/Connection/ConnectionFactory.php @@ -159,6 +159,11 @@ protected static function getReadWriteTimeout(AMQPConnectionConfig $config): flo protected static function getSslOptions(AMQPConnectionConfig $config): array { + $securityLevel = null; + if (method_exists($config, 'getSslSecurityLevel')) { + $securityLevel = $config->getSslSecurityLevel(); + } + return array_filter([ 'cafile' => $config->getSslCaCert(), 'capath' => $config->getSslCaPath(), @@ -168,7 +173,7 @@ protected static function getSslOptions(AMQPConnectionConfig $config): array 'verify_peer_name' => $config->getSslVerifyName(), 'passphrase' => $config->getSslPassPhrase(), 'ciphers' => $config->getSslCiphers(), - 'security_level' => $config->getSslSecurityLevel(), + 'security_level' => $securityLevel, ], static function ($value) { return $value !== null; }); From 1a7339bef56f95731e923e11c5e0f34308310950 Mon Sep 17 00:00:00 2001 From: Seb Date: Mon, 2 Oct 2023 09:36:37 -0700 Subject: [PATCH 328/375] compat --- composer.json | 2 +- src/Queue/Connection/ConnectionFactory.php | 7 +------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/composer.json b/composer.json index 2897c08e..7c0cfe9c 100644 --- a/composer.json +++ b/composer.json @@ -12,7 +12,7 @@ "php": "^8.0", "ext-json": "*", "illuminate/queue": "^9.0|^10.0", - "php-amqplib/php-amqplib": "^v3.3" + "php-amqplib/php-amqplib": "^v3.4" }, "require-dev": { "phpunit/phpunit": "^9.3", diff --git a/src/Queue/Connection/ConnectionFactory.php b/src/Queue/Connection/ConnectionFactory.php index 7dd95e99..f5ccad0e 100644 --- a/src/Queue/Connection/ConnectionFactory.php +++ b/src/Queue/Connection/ConnectionFactory.php @@ -159,11 +159,6 @@ protected static function getReadWriteTimeout(AMQPConnectionConfig $config): flo protected static function getSslOptions(AMQPConnectionConfig $config): array { - $securityLevel = null; - if (method_exists($config, 'getSslSecurityLevel')) { - $securityLevel = $config->getSslSecurityLevel(); - } - return array_filter([ 'cafile' => $config->getSslCaCert(), 'capath' => $config->getSslCaPath(), @@ -173,7 +168,7 @@ protected static function getSslOptions(AMQPConnectionConfig $config): array 'verify_peer_name' => $config->getSslVerifyName(), 'passphrase' => $config->getSslPassPhrase(), 'ciphers' => $config->getSslCiphers(), - 'security_level' => $securityLevel, + 'security_level' => $config->getSslSecurityLevel(), ], static function ($value) { return $value !== null; }); From 128b0d4620f44b188ff517d6b26d3d8b0f7c1405 Mon Sep 17 00:00:00 2001 From: Seb Date: Mon, 2 Oct 2023 09:39:05 -0700 Subject: [PATCH 329/375] compat --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 7c0cfe9c..83767040 100644 --- a/composer.json +++ b/composer.json @@ -12,7 +12,7 @@ "php": "^8.0", "ext-json": "*", "illuminate/queue": "^9.0|^10.0", - "php-amqplib/php-amqplib": "^v3.4" + "php-amqplib/php-amqplib": "^v3.5" }, "require-dev": { "phpunit/phpunit": "^9.3", From 83a089e02b9d5de5b5545a235994938bde9600bd Mon Sep 17 00:00:00 2001 From: Seb Date: Mon, 2 Oct 2023 09:41:36 -0700 Subject: [PATCH 330/375] higher --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 83767040..f487d4e9 100644 --- a/composer.json +++ b/composer.json @@ -12,7 +12,7 @@ "php": "^8.0", "ext-json": "*", "illuminate/queue": "^9.0|^10.0", - "php-amqplib/php-amqplib": "^v3.5" + "php-amqplib/php-amqplib": "^v3.5.1" }, "require-dev": { "phpunit/phpunit": "^9.3", From 3669445c8def3554b552bf3659ad8833c481666e Mon Sep 17 00:00:00 2001 From: Seb Date: Mon, 2 Oct 2023 09:50:36 -0700 Subject: [PATCH 331/375] higher --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index f487d4e9..e7167c1a 100644 --- a/composer.json +++ b/composer.json @@ -12,7 +12,7 @@ "php": "^8.0", "ext-json": "*", "illuminate/queue": "^9.0|^10.0", - "php-amqplib/php-amqplib": "^v3.5.1" + "php-amqplib/php-amqplib": "^v3.5.2" }, "require-dev": { "phpunit/phpunit": "^9.3", From 57fba78ae1d405c19bb6ae37145f47966282c9e4 Mon Sep 17 00:00:00 2001 From: Emiel Bom <11626777+adm-bome@users.noreply.github.com> Date: Wed, 24 Jan 2024 14:32:45 +0100 Subject: [PATCH 332/375] Ignore errors from nullable_type_declarations Quick fix for the pint tests --- pint.json | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 pint.json diff --git a/pint.json b/pint.json new file mode 100644 index 00000000..751c2203 --- /dev/null +++ b/pint.json @@ -0,0 +1,8 @@ +{ + "preset": "laravel", + "rules": { + "nullable_type_declaration_for_default_null_value": { + "use_nullable_type_declaration": false + } + } +} From 2695f785096f08a8535e16697b6c83c4c65fb4ed Mon Sep 17 00:00:00 2001 From: Emiel Bom <11626777+adm-bome@users.noreply.github.com> Date: Wed, 24 Jan 2024 16:42:06 +0100 Subject: [PATCH 333/375] can't use network_protocol for ssl connections --- src/Queue/Connection/ConnectionFactory.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Queue/Connection/ConnectionFactory.php b/src/Queue/Connection/ConnectionFactory.php index f5ccad0e..f531e761 100644 --- a/src/Queue/Connection/ConnectionFactory.php +++ b/src/Queue/Connection/ConnectionFactory.php @@ -126,7 +126,6 @@ protected static function createStreamConnection($connection, AMQPConnectionConf 'keepalive' => $config->isKeepalive(), 'heartbeat' => $config->getHeartbeat(), ], - $config->getNetworkProtocol(), $config ); } From 1475380174bf8c9183d5490fe737f285a4ddb0ae Mon Sep 17 00:00:00 2001 From: Emiel Bom <11626777+adm-bome@users.noreply.github.com> Date: Wed, 24 Jan 2024 16:51:10 +0100 Subject: [PATCH 334/375] skip test --- tests/Feature/TestCase.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/Feature/TestCase.php b/tests/Feature/TestCase.php index 299e7024..fc69568a 100644 --- a/tests/Feature/TestCase.php +++ b/tests/Feature/TestCase.php @@ -96,6 +96,8 @@ public function testPush(): void public function testPushAfterCommit(): void { + $this->markTestSkipped(); + $transaction = new DatabaseTransactionsManager; $this->app->singleton('db.transactions', function ($app) use ($transaction) { From 806a59d2f5ffd04c3c2a421f9c5fa6d929fc4764 Mon Sep 17 00:00:00 2001 From: Emiel Bom <11626777+adm-bome@users.noreply.github.com> Date: Wed, 24 Jan 2024 16:55:24 +0100 Subject: [PATCH 335/375] Update ConnectorTest.php --- tests/Feature/ConnectorTest.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/Feature/ConnectorTest.php b/tests/Feature/ConnectorTest.php index 91660ad2..8b8ecd82 100644 --- a/tests/Feature/ConnectorTest.php +++ b/tests/Feature/ConnectorTest.php @@ -144,6 +144,8 @@ public function testSslConnection(): void // Test to validate ssl connection params public function testNoVerificationSslConnection(): void { + $this->markTestSkipped(); + $this->app['config']->set('queue.connections.rabbitmq', [ 'driver' => 'rabbitmq', 'queue' => env('RABBITMQ_QUEUE', 'default'), From 089e72d162b7cb554d80a6ecbb85f0d9b00a8baa Mon Sep 17 00:00:00 2001 From: Emiel Bom <11626777+adm-bome@users.noreply.github.com> Date: Thu, 25 Jan 2024 12:37:47 +0100 Subject: [PATCH 336/375] changed signature method commit between laravel versions 9 and 10, the signature of the method `commit` within the `DatabaseTransactionsManager:class` was changed. --- tests/Feature/TestCase.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/Feature/TestCase.php b/tests/Feature/TestCase.php index fc69568a..c49d8608 100644 --- a/tests/Feature/TestCase.php +++ b/tests/Feature/TestCase.php @@ -96,8 +96,6 @@ public function testPush(): void public function testPushAfterCommit(): void { - $this->markTestSkipped(); - $transaction = new DatabaseTransactionsManager; $this->app->singleton('db.transactions', function ($app) use ($transaction) { @@ -112,7 +110,7 @@ public function testPushAfterCommit(): void $this->assertSame(0, Queue::size()); $this->assertNull(Queue::pop()); - $transaction->commit('FakeDBConnection'); + $transaction->commit('FakeDBConnection', 1, 0); sleep(1); From e04ff6b41e626ef2d0457f7b7154cfe856764d02 Mon Sep 17 00:00:00 2001 From: Emiel Bom <11626777+adm-bome@users.noreply.github.com> Date: Thu, 25 Jan 2024 12:49:33 +0100 Subject: [PATCH 337/375] changed the php-amqplib version to ^3.6 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index e7167c1a..cd2e6898 100644 --- a/composer.json +++ b/composer.json @@ -12,7 +12,7 @@ "php": "^8.0", "ext-json": "*", "illuminate/queue": "^9.0|^10.0", - "php-amqplib/php-amqplib": "^v3.5.2" + "php-amqplib/php-amqplib": "^v3.6" }, "require-dev": { "phpunit/phpunit": "^9.3", From a7de79e86fd8e2b802ccd8be99979e7165b1f5a8 Mon Sep 17 00:00:00 2001 From: Emiel Bom <11626777+adm-bome@users.noreply.github.com> Date: Thu, 25 Jan 2024 12:52:43 +0100 Subject: [PATCH 338/375] Update ConnectorTest.php --- tests/Feature/ConnectorTest.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/Feature/ConnectorTest.php b/tests/Feature/ConnectorTest.php index 8b8ecd82..91660ad2 100644 --- a/tests/Feature/ConnectorTest.php +++ b/tests/Feature/ConnectorTest.php @@ -144,8 +144,6 @@ public function testSslConnection(): void // Test to validate ssl connection params public function testNoVerificationSslConnection(): void { - $this->markTestSkipped(); - $this->app['config']->set('queue.connections.rabbitmq', [ 'driver' => 'rabbitmq', 'queue' => env('RABBITMQ_QUEUE', 'default'), From ba4dd0e31af0730240a10992b69e04785d59d616 Mon Sep 17 00:00:00 2001 From: Emiel Bom <11626777+adm-bome@users.noreply.github.com> Date: Fri, 26 Jan 2024 13:15:46 +0100 Subject: [PATCH 339/375] Fix for errors on $currentJob property RabbitMQQueue::$currentJob must not be accessed before initialization Fixes for issue: #572 #555 --- src/Queue/RabbitMQQueue.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index 0bf2e608..19c4efb2 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -54,7 +54,7 @@ class RabbitMQQueue extends Queue implements QueueContract, RabbitMQQueueContrac /** * Current job being processed. */ - protected RabbitMQJob $currentJob; + protected ?RabbitMQJob $currentJob = null; /** * Holds the Configuration From cfca9419eac07def75c5ca2514e21002deb327fa Mon Sep 17 00:00:00 2001 From: Emiel Bom <11626777+adm-bome@users.noreply.github.com> Date: Mon, 29 Jan 2024 10:08:56 +0100 Subject: [PATCH 340/375] Update README.md Build status icon: link/urls altered --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b7b51350..adb7b2e9 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ RabbitMQ Queue driver for Laravel ====================== [![Latest Stable Version](https://poser.pugx.org/vladimir-yuldashev/laravel-queue-rabbitmq/v/stable?format=flat-square)](https://packagist.org/packages/vladimir-yuldashev/laravel-queue-rabbitmq) -[![Build Status](https://github.com/vyuldashev/laravel-queue-rabbitmq/workflows/Tests/badge.svg)](https://github.com/vyuldashev/laravel-queue-rabbitmq/actions) +[![Build Status](https://github.com/vyuldashev/laravel-queue-rabbitmq/actions/workflows/tests.yml/badge.svg?branch=master)](https://github.com/vyuldashev/laravel-queue-rabbitmq/actions/workflows/tests.yml) [![Total Downloads](https://poser.pugx.org/vladimir-yuldashev/laravel-queue-rabbitmq/downloads?format=flat-square)](https://packagist.org/packages/vladimir-yuldashev/laravel-queue-rabbitmq) [![License](https://poser.pugx.org/vladimir-yuldashev/laravel-queue-rabbitmq/license?format=flat-square)](https://packagist.org/packages/vladimir-yuldashev/laravel-queue-rabbitmq) From 3b23045bb9ad27f5a47267cab34bc99989151e70 Mon Sep 17 00:00:00 2001 From: Seb Date: Tue, 12 Mar 2024 13:07:35 -0700 Subject: [PATCH 341/375] laravel 11 test --- .github/workflows/tests.yml | 2 +- composer.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 37c80375..de605b46 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -15,7 +15,7 @@ jobs: matrix: php: ['8.0', '8.1', '8.2'] stability: ['prefer-lowest', prefer-stable] - laravel: ['^9.0', '^10.0'] + laravel: ['^9.0', '^10.0', '^11.0'] exclude: - php: '8.0' laravel: '^10.0' diff --git a/composer.json b/composer.json index cd2e6898..4e70a3eb 100644 --- a/composer.json +++ b/composer.json @@ -11,7 +11,7 @@ "require": { "php": "^8.0", "ext-json": "*", - "illuminate/queue": "^9.0|^10.0", + "illuminate/queue": "^9.0|^10.0|^11.0", "php-amqplib/php-amqplib": "^v3.6" }, "require-dev": { From 21b4bc11e602249559de7d656ce6a9335395ef50 Mon Sep 17 00:00:00 2001 From: Seb Date: Tue, 12 Mar 2024 13:10:02 -0700 Subject: [PATCH 342/375] missed one --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 4e70a3eb..5d7d43bd 100644 --- a/composer.json +++ b/composer.json @@ -20,7 +20,7 @@ "laravel/horizon": "^5.0", "orchestra/testbench": "^7.0|^8.0", "laravel/pint": "^1.2", - "laravel/framework": "^9.0|^10.0" + "laravel/framework": "^9.0|^10.0|^11.0" }, "autoload": { "psr-4": { From 5ecbc176686d884b1167a751278fd9816d0a7389 Mon Sep 17 00:00:00 2001 From: Seb Date: Tue, 12 Mar 2024 13:12:01 -0700 Subject: [PATCH 343/375] the matrix --- .github/workflows/tests.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index de605b46..0949eeb0 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -19,6 +19,10 @@ jobs: exclude: - php: '8.0' laravel: '^10.0' + - php: '8.0' + laravel: '^11.0' + - php: '8.1' + laravel: '^11.0' name: 'PHP ${{ matrix.php }} - Laravel: ${{matrix.laravel}} - ${{ matrix.stability }}' From a453471dc98692114c03cb0d945adeadc0ac9869 Mon Sep 17 00:00:00 2001 From: Seb Date: Tue, 12 Mar 2024 13:24:11 -0700 Subject: [PATCH 344/375] orchestra --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 5d7d43bd..b9e327d4 100644 --- a/composer.json +++ b/composer.json @@ -18,7 +18,7 @@ "phpunit/phpunit": "^9.3", "mockery/mockery": "^1.0", "laravel/horizon": "^5.0", - "orchestra/testbench": "^7.0|^8.0", + "orchestra/testbench": "^7.0|^8.0|^9.0", "laravel/pint": "^1.2", "laravel/framework": "^9.0|^10.0|^11.0" }, From a40b400d4f408eb5d182013d69f74e292aae48a8 Mon Sep 17 00:00:00 2001 From: Seb Date: Wed, 13 Mar 2024 09:41:19 -0700 Subject: [PATCH 345/375] reflop From 261d236242c6b5ce61e9a3a90f482f527d700baf Mon Sep 17 00:00:00 2001 From: Seb Date: Wed, 13 Mar 2024 09:57:08 -0700 Subject: [PATCH 346/375] phpunit --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index b9e327d4..d0ec318a 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,7 @@ "php-amqplib/php-amqplib": "^v3.6" }, "require-dev": { - "phpunit/phpunit": "^9.3", + "phpunit/phpunit": "^9.3 || ^10.5 || ^11.0.1", "mockery/mockery": "^1.0", "laravel/horizon": "^5.0", "orchestra/testbench": "^7.0|^8.0|^9.0", From 4d96f07f91bc088cf66f247821c4d1ff12302fe9 Mon Sep 17 00:00:00 2001 From: Seb Date: Wed, 13 Mar 2024 10:06:05 -0700 Subject: [PATCH 347/375] phpunit --- .github/workflows/tests.yml | 2 +- composer.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 0949eeb0..79574284 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -54,4 +54,4 @@ jobs: run: ./vendor/bin/pint --test - name: Execute tests - run: sleep 10 && vendor/bin/phpunit --verbose + run: sleep 10 && vendor/bin/phpunit --debug diff --git a/composer.json b/composer.json index d0ec318a..10480139 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,7 @@ "php-amqplib/php-amqplib": "^v3.6" }, "require-dev": { - "phpunit/phpunit": "^9.3 || ^10.5 || ^11.0.1", + "phpunit/phpunit": "^9.3 || ^10.5 || ^11.0", "mockery/mockery": "^1.0", "laravel/horizon": "^5.0", "orchestra/testbench": "^7.0|^8.0|^9.0", From 7947a32523f9d2c43391ba6d93bdd7294df6c3d8 Mon Sep 17 00:00:00 2001 From: Seb Date: Wed, 13 Mar 2024 10:41:50 -0700 Subject: [PATCH 348/375] phpunit --- .github/workflows/tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 79574284..20254e12 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -14,7 +14,7 @@ jobs: fail-fast: true matrix: php: ['8.0', '8.1', '8.2'] - stability: ['prefer-lowest', prefer-stable] + stability: ['prefer-lowest', 'prefer-stable'] laravel: ['^9.0', '^10.0', '^11.0'] exclude: - php: '8.0' @@ -54,4 +54,4 @@ jobs: run: ./vendor/bin/pint --test - name: Execute tests - run: sleep 10 && vendor/bin/phpunit --debug + run: sleep 10 && (vendor/bin/phpunit --debug || vendor/bin/phpunit --verbose) From 173186643e650b91b6eef609f21330a2c4c8211c Mon Sep 17 00:00:00 2001 From: Seb Date: Wed, 13 Mar 2024 10:46:00 -0700 Subject: [PATCH 349/375] phpunit --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 20254e12..fd06a8b9 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -54,4 +54,4 @@ jobs: run: ./vendor/bin/pint --test - name: Execute tests - run: sleep 10 && (vendor/bin/phpunit --debug || vendor/bin/phpunit --verbose) + run: sleep 10 && vendor/bin/phpunit From 6e78b31b1b5ded11a44de41f227d2eb3ad4018f1 Mon Sep 17 00:00:00 2001 From: Seb Date: Wed, 13 Mar 2024 11:04:55 -0700 Subject: [PATCH 350/375] skip10 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 10480139..37a7aac9 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,7 @@ "php-amqplib/php-amqplib": "^v3.6" }, "require-dev": { - "phpunit/phpunit": "^9.3 || ^10.5 || ^11.0", + "phpunit/phpunit": "^9.3|^11.0", "mockery/mockery": "^1.0", "laravel/horizon": "^5.0", "orchestra/testbench": "^7.0|^8.0|^9.0", From ea5b087b3d9feeba2dc127a7ce6363c3595edfc1 Mon Sep 17 00:00:00 2001 From: Seb Date: Wed, 13 Mar 2024 11:08:28 -0700 Subject: [PATCH 351/375] config format --- phpunit.xml.dist | 48 +++++++++++++++++++++++------------------------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 7e166ab8..57791a39 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,27 +1,25 @@ - - - - src/ - - - - - - - - ./tests/ - - - - - - - - - + + + + + + + + + ./tests/ + + + + + + + + + + + + src/ + + From 3c33cddfb65bf156c310a9f1e86c83d7cf868435 Mon Sep 17 00:00:00 2001 From: Seb Date: Wed, 13 Mar 2024 11:13:53 -0700 Subject: [PATCH 352/375] nogroups --- tests/Feature/SslQueueTest.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/Feature/SslQueueTest.php b/tests/Feature/SslQueueTest.php index 7233ee09..e9c460ea 100644 --- a/tests/Feature/SslQueueTest.php +++ b/tests/Feature/SslQueueTest.php @@ -4,9 +4,6 @@ use PhpAmqpLib\Connection\AMQPSSLConnection; -/** - * @group functional - */ class SslQueueTest extends TestCase { public function setUp(): void From 0391b4155398d5c54ab2cfa5b2c14dbfb404d27f Mon Sep 17 00:00:00 2001 From: Seb Date: Wed, 13 Mar 2024 11:22:02 -0700 Subject: [PATCH 353/375] only keep supported versions --- .github/workflows/tests.yml | 9 +++------ composer.json | 2 +- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index fd06a8b9..7baac992 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -13,14 +13,10 @@ jobs: strategy: fail-fast: true matrix: - php: ['8.0', '8.1', '8.2'] + php: ['8.1', '8.2', '8.3'] stability: ['prefer-lowest', 'prefer-stable'] - laravel: ['^9.0', '^10.0', '^11.0'] + laravel: ['^10.0', '^11.0'] exclude: - - php: '8.0' - laravel: '^10.0' - - php: '8.0' - laravel: '^11.0' - php: '8.1' laravel: '^11.0' @@ -54,4 +50,5 @@ jobs: run: ./vendor/bin/pint --test - name: Execute tests + if: run: sleep 10 && vendor/bin/phpunit diff --git a/composer.json b/composer.json index 37a7aac9..0f964596 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,7 @@ "php-amqplib/php-amqplib": "^v3.6" }, "require-dev": { - "phpunit/phpunit": "^9.3|^11.0", + "phpunit/phpunit": "^11.0", "mockery/mockery": "^1.0", "laravel/horizon": "^5.0", "orchestra/testbench": "^7.0|^8.0|^9.0", From 6612effdfcf6cc923982c2781e09e3659e93334f Mon Sep 17 00:00:00 2001 From: Seb Date: Wed, 13 Mar 2024 11:23:10 -0700 Subject: [PATCH 354/375] whoopsie --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 0f964596..a4cb0504 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,7 @@ "php-amqplib/php-amqplib": "^v3.6" }, "require-dev": { - "phpunit/phpunit": "^11.0", + "phpunit/phpunit": "^10.0|^11.0", "mockery/mockery": "^1.0", "laravel/horizon": "^5.0", "orchestra/testbench": "^7.0|^8.0|^9.0", From a4065e24bd33500513785a888f0197c41aeff2e0 Mon Sep 17 00:00:00 2001 From: Seb Date: Wed, 13 Mar 2024 11:28:47 -0700 Subject: [PATCH 355/375] who knows --- composer.json | 2 +- phpunit.xml.dist | 29 +++++++++++++++++++---------- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/composer.json b/composer.json index a4cb0504..1a91d725 100644 --- a/composer.json +++ b/composer.json @@ -11,7 +11,7 @@ "require": { "php": "^8.0", "ext-json": "*", - "illuminate/queue": "^9.0|^10.0|^11.0", + "illuminate/queue": "^10.0|^11.0", "php-amqplib/php-amqplib": "^v3.6" }, "require-dev": { diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 57791a39..5d537691 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,20 +1,29 @@ - - - - - - + ./tests/ - - - - + + + + From 453324a0bcca2c8a781972861f789cb1e0db9eb7 Mon Sep 17 00:00:00 2001 From: Seb Date: Wed, 13 Mar 2024 11:37:08 -0700 Subject: [PATCH 356/375] Happify phpunit --- tests/{TestCase.php => BaseTestCase.php} | 4 ++-- tests/Feature/{TestCase.php => BaseTestCase.php} | 4 ++-- tests/Feature/ConnectorTest.php | 2 +- tests/Feature/QueueTest.php | 2 +- tests/Feature/SslQueueTest.php | 2 +- tests/Functional/{TestCase.php => BaseTestCase.php} | 4 ++-- tests/Functional/RabbitMQQueueTest.php | 1 - 7 files changed, 9 insertions(+), 10 deletions(-) rename tests/{TestCase.php => BaseTestCase.php} (94%) rename tests/Feature/{TestCase.php => BaseTestCase.php} (98%) rename tests/Functional/{TestCase.php => BaseTestCase.php} (98%) diff --git a/tests/TestCase.php b/tests/BaseTestCase.php similarity index 94% rename from tests/TestCase.php rename to tests/BaseTestCase.php index 7d50fa67..a2ad4983 100644 --- a/tests/TestCase.php +++ b/tests/BaseTestCase.php @@ -3,12 +3,12 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Tests; use Illuminate\Support\Facades\Queue; -use Orchestra\Testbench\TestCase as BaseTestCase; +use Orchestra\Testbench\TestCase; use PhpAmqpLib\Connection\AMQPLazyConnection; use VladimirYuldashev\LaravelQueueRabbitMQ\LaravelQueueRabbitMQServiceProvider; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue; -abstract class TestCase extends BaseTestCase +abstract class BaseTestCase extends TestCase { protected function getPackageProviders($app): array { diff --git a/tests/Feature/TestCase.php b/tests/Feature/BaseTestCase.php similarity index 98% rename from tests/Feature/TestCase.php rename to tests/Feature/BaseTestCase.php index c49d8608..11cedfb1 100644 --- a/tests/Feature/TestCase.php +++ b/tests/Feature/BaseTestCase.php @@ -8,10 +8,10 @@ use PhpAmqpLib\Exception\AMQPProtocolChannelException; use RuntimeException; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Jobs\RabbitMQJob; +use VladimirYuldashev\LaravelQueueRabbitMQ\Tests\BaseTestCase as TestCase; use VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Mocks\TestJob; -use VladimirYuldashev\LaravelQueueRabbitMQ\Tests\TestCase as BaseTestCase; -abstract class TestCase extends BaseTestCase +abstract class BaseTestCase extends TestCase { /** * @throws AMQPProtocolChannelException diff --git a/tests/Feature/ConnectorTest.php b/tests/Feature/ConnectorTest.php index 91660ad2..263fbd90 100644 --- a/tests/Feature/ConnectorTest.php +++ b/tests/Feature/ConnectorTest.php @@ -10,7 +10,7 @@ use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue; use VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Mocks\TestSSLConnection; -class ConnectorTest extends \VladimirYuldashev\LaravelQueueRabbitMQ\Tests\TestCase +class ConnectorTest extends \VladimirYuldashev\LaravelQueueRabbitMQ\Tests\BaseTestCase { public function testLazyConnection(): void { diff --git a/tests/Feature/QueueTest.php b/tests/Feature/QueueTest.php index 5ecfb978..3c5ed540 100644 --- a/tests/Feature/QueueTest.php +++ b/tests/Feature/QueueTest.php @@ -8,7 +8,7 @@ use PhpAmqpLib\Exception\AMQPProtocolChannelException; use VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Mocks\TestJob; -class QueueTest extends TestCase +class QueueTest extends BaseTestCase { public function setUp(): void { diff --git a/tests/Feature/SslQueueTest.php b/tests/Feature/SslQueueTest.php index e9c460ea..96383bbc 100644 --- a/tests/Feature/SslQueueTest.php +++ b/tests/Feature/SslQueueTest.php @@ -4,7 +4,7 @@ use PhpAmqpLib\Connection\AMQPSSLConnection; -class SslQueueTest extends TestCase +class SslQueueTest extends BaseTestCase { public function setUp(): void { diff --git a/tests/Functional/TestCase.php b/tests/Functional/BaseTestCase.php similarity index 98% rename from tests/Functional/TestCase.php rename to tests/Functional/BaseTestCase.php index 8b843561..0ab263b8 100644 --- a/tests/Functional/TestCase.php +++ b/tests/Functional/BaseTestCase.php @@ -6,9 +6,9 @@ use PhpAmqpLib\Channel\AMQPChannel; use ReflectionClass; use ReflectionException; -use VladimirYuldashev\LaravelQueueRabbitMQ\Tests\TestCase as BaseTestCase; +use VladimirYuldashev\LaravelQueueRabbitMQ\Tests\BaseTestCase as TestCase; -abstract class TestCase extends BaseTestCase +abstract class BaseTestCase extends TestCase { protected function getEnvironmentSetUp($app): void { diff --git a/tests/Functional/RabbitMQQueueTest.php b/tests/Functional/RabbitMQQueueTest.php index 7a106a08..936857ba 100644 --- a/tests/Functional/RabbitMQQueueTest.php +++ b/tests/Functional/RabbitMQQueueTest.php @@ -5,7 +5,6 @@ use Illuminate\Support\Str; use PhpAmqpLib\Exchange\AMQPExchangeType; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue; -use VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Functional\TestCase as BaseTestCase; class RabbitMQQueueTest extends BaseTestCase { From 74c05cdf72bc8f18b0bb5d8a5d9143c42cc60e66 Mon Sep 17 00:00:00 2001 From: Seb Date: Wed, 13 Mar 2024 11:55:48 -0700 Subject: [PATCH 357/375] Revert "Happify phpunit" This reverts commit 453324a0bcca2c8a781972861f789cb1e0db9eb7. --- tests/Feature/ConnectorTest.php | 2 +- tests/Feature/QueueTest.php | 2 +- tests/Feature/SslQueueTest.php | 2 +- tests/Feature/{BaseTestCase.php => TestCase.php} | 4 ++-- tests/Functional/RabbitMQQueueTest.php | 1 + tests/Functional/{BaseTestCase.php => TestCase.php} | 4 ++-- tests/{BaseTestCase.php => TestCase.php} | 4 ++-- 7 files changed, 10 insertions(+), 9 deletions(-) rename tests/Feature/{BaseTestCase.php => TestCase.php} (98%) rename tests/Functional/{BaseTestCase.php => TestCase.php} (98%) rename tests/{BaseTestCase.php => TestCase.php} (94%) diff --git a/tests/Feature/ConnectorTest.php b/tests/Feature/ConnectorTest.php index 263fbd90..91660ad2 100644 --- a/tests/Feature/ConnectorTest.php +++ b/tests/Feature/ConnectorTest.php @@ -10,7 +10,7 @@ use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue; use VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Mocks\TestSSLConnection; -class ConnectorTest extends \VladimirYuldashev\LaravelQueueRabbitMQ\Tests\BaseTestCase +class ConnectorTest extends \VladimirYuldashev\LaravelQueueRabbitMQ\Tests\TestCase { public function testLazyConnection(): void { diff --git a/tests/Feature/QueueTest.php b/tests/Feature/QueueTest.php index 3c5ed540..5ecfb978 100644 --- a/tests/Feature/QueueTest.php +++ b/tests/Feature/QueueTest.php @@ -8,7 +8,7 @@ use PhpAmqpLib\Exception\AMQPProtocolChannelException; use VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Mocks\TestJob; -class QueueTest extends BaseTestCase +class QueueTest extends TestCase { public function setUp(): void { diff --git a/tests/Feature/SslQueueTest.php b/tests/Feature/SslQueueTest.php index 96383bbc..e9c460ea 100644 --- a/tests/Feature/SslQueueTest.php +++ b/tests/Feature/SslQueueTest.php @@ -4,7 +4,7 @@ use PhpAmqpLib\Connection\AMQPSSLConnection; -class SslQueueTest extends BaseTestCase +class SslQueueTest extends TestCase { public function setUp(): void { diff --git a/tests/Feature/BaseTestCase.php b/tests/Feature/TestCase.php similarity index 98% rename from tests/Feature/BaseTestCase.php rename to tests/Feature/TestCase.php index 11cedfb1..c49d8608 100644 --- a/tests/Feature/BaseTestCase.php +++ b/tests/Feature/TestCase.php @@ -8,10 +8,10 @@ use PhpAmqpLib\Exception\AMQPProtocolChannelException; use RuntimeException; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Jobs\RabbitMQJob; -use VladimirYuldashev\LaravelQueueRabbitMQ\Tests\BaseTestCase as TestCase; use VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Mocks\TestJob; +use VladimirYuldashev\LaravelQueueRabbitMQ\Tests\TestCase as BaseTestCase; -abstract class BaseTestCase extends TestCase +abstract class TestCase extends BaseTestCase { /** * @throws AMQPProtocolChannelException diff --git a/tests/Functional/RabbitMQQueueTest.php b/tests/Functional/RabbitMQQueueTest.php index 936857ba..7a106a08 100644 --- a/tests/Functional/RabbitMQQueueTest.php +++ b/tests/Functional/RabbitMQQueueTest.php @@ -5,6 +5,7 @@ use Illuminate\Support\Str; use PhpAmqpLib\Exchange\AMQPExchangeType; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue; +use VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Functional\TestCase as BaseTestCase; class RabbitMQQueueTest extends BaseTestCase { diff --git a/tests/Functional/BaseTestCase.php b/tests/Functional/TestCase.php similarity index 98% rename from tests/Functional/BaseTestCase.php rename to tests/Functional/TestCase.php index 0ab263b8..8b843561 100644 --- a/tests/Functional/BaseTestCase.php +++ b/tests/Functional/TestCase.php @@ -6,9 +6,9 @@ use PhpAmqpLib\Channel\AMQPChannel; use ReflectionClass; use ReflectionException; -use VladimirYuldashev\LaravelQueueRabbitMQ\Tests\BaseTestCase as TestCase; +use VladimirYuldashev\LaravelQueueRabbitMQ\Tests\TestCase as BaseTestCase; -abstract class BaseTestCase extends TestCase +abstract class TestCase extends BaseTestCase { protected function getEnvironmentSetUp($app): void { diff --git a/tests/BaseTestCase.php b/tests/TestCase.php similarity index 94% rename from tests/BaseTestCase.php rename to tests/TestCase.php index a2ad4983..7d50fa67 100644 --- a/tests/BaseTestCase.php +++ b/tests/TestCase.php @@ -3,12 +3,12 @@ namespace VladimirYuldashev\LaravelQueueRabbitMQ\Tests; use Illuminate\Support\Facades\Queue; -use Orchestra\Testbench\TestCase; +use Orchestra\Testbench\TestCase as BaseTestCase; use PhpAmqpLib\Connection\AMQPLazyConnection; use VladimirYuldashev\LaravelQueueRabbitMQ\LaravelQueueRabbitMQServiceProvider; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue; -abstract class BaseTestCase extends TestCase +abstract class TestCase extends BaseTestCase { protected function getPackageProviders($app): array { From 2408bba9f11f20dc07252584a6efeea5a0dfed64 Mon Sep 17 00:00:00 2001 From: Seb Date: Wed, 13 Mar 2024 11:57:36 -0700 Subject: [PATCH 358/375] only use test files --- phpunit.xml.dist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 5d537691..da6c9d37 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -12,7 +12,7 @@ backupStaticProperties="false"> - ./tests/ + ./tests/ From 75fa317e65f2889eca617b7c589dbfd276d95a69 Mon Sep 17 00:00:00 2001 From: Seb Date: Wed, 13 Mar 2024 12:13:31 -0700 Subject: [PATCH 359/375] yo --- phpunit.xml.dist | 57 +++++++++++++++++++++--------------------------- 1 file changed, 25 insertions(+), 32 deletions(-) diff --git a/phpunit.xml.dist b/phpunit.xml.dist index da6c9d37..c3fc3599 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,34 +1,27 @@ - - - - ./tests/ - - - - - - - - - - - - src/ - - + + + + src/ + + + + + + + + ./tests/ + + + + + + + + + From dd97e4b3cb30b2f3260fec12f177f8dfb93ffb40 Mon Sep 17 00:00:00 2001 From: Seb Date: Wed, 13 Mar 2024 12:16:30 -0700 Subject: [PATCH 360/375] fixie --- phpunit.xml.dist | 48 +++++++++++++++++++++++------------------------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/phpunit.xml.dist b/phpunit.xml.dist index c3fc3599..219f4c66 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,27 +1,25 @@ - - - - src/ - - - - - - - - ./tests/ - - - - - - - - - + + + + + + + + + ./tests/ + + + + + + + + + + + + src/ + + From 3f8df65433a1bda50a0b6f7077e858e4523a6861 Mon Sep 17 00:00:00 2001 From: Seb Date: Wed, 13 Mar 2024 12:19:13 -0700 Subject: [PATCH 361/375] nocov --- phpunit.xml.dist | 5 ----- 1 file changed, 5 deletions(-) diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 219f4c66..10a342a9 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,10 +1,5 @@ - - - - - ./tests/ From f550805d944bf130a9e607e0eb061de0b2f38cab Mon Sep 17 00:00:00 2001 From: Seb Date: Wed, 13 Mar 2024 12:21:36 -0700 Subject: [PATCH 362/375] nosource --- phpunit.xml.dist | 5 ----- 1 file changed, 5 deletions(-) diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 10a342a9..71be4e98 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -12,9 +12,4 @@ - - - src/ - - From 83c646ea55f7350d1549a107e39230371c547055 Mon Sep 17 00:00:00 2001 From: Seb Date: Wed, 13 Mar 2024 12:26:41 -0700 Subject: [PATCH 363/375] mew --- .github/workflows/tests.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 7baac992..fccb0608 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -50,5 +50,4 @@ jobs: run: ./vendor/bin/pint --test - name: Execute tests - if: run: sleep 10 && vendor/bin/phpunit From f87aa9d63b39eb9fb14433f29f2a05db5eb35e4c Mon Sep 17 00:00:00 2001 From: Seb Date: Thu, 14 Mar 2024 08:29:16 -0700 Subject: [PATCH 364/375] changelog --- CHANGELOG-14x.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 CHANGELOG-14x.md diff --git a/CHANGELOG-14x.md b/CHANGELOG-14x.md new file mode 100644 index 00000000..c94b3dd2 --- /dev/null +++ b/CHANGELOG-14x.md @@ -0,0 +1,8 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +## [unreleased](https://github.com/vyuldashev/laravel-queue-rabbitmq/compare/v13.3.0...master) + +## [14.0.0] +- First release compatible with Laravel 11 From fc341b3f19496bc1c66cd38a665c2d2da8b8baaf Mon Sep 17 00:00:00 2001 From: therecluse26 <8239106+therecluse26@users.noreply.github.com> Date: Sun, 21 Apr 2024 15:26:25 -0400 Subject: [PATCH 365/375] Allow for encrypted command data Added a bugfix to handle fetching priority from encrypted command data if the job class implements `ShouldBeEncrypted` --- src/Queue/RabbitMQQueue.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index 19c4efb2..6cb2126f 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -525,6 +525,11 @@ protected function createMessage($payload, int $attempts = 0): array } if (isset($currentPayload['data']['command'])) { + // If the command data is encrypted, decrypt it first before attempting to unserialize + if (in_array(ShouldBeEncrypted::class, class_implements($currentPayload['data']['commandName']))) { + $currentPayload['data']['command'] = decrypt($currentPayload['data']['command']); + } + $commandData = unserialize($currentPayload['data']['command']); if (property_exists($commandData, 'priority')) { $properties['priority'] = $commandData->priority; From 9bd427a6d281d6e0f0fa576ae04435100380b4dd Mon Sep 17 00:00:00 2001 From: therecluse26 Date: Wed, 24 Apr 2024 17:28:09 -0400 Subject: [PATCH 366/375] Added encryption key, tests and requested updates --- phpunit.xml.dist | 1 + src/Queue/RabbitMQQueue.php | 8 ++- tests/Feature/TestCase.php | 98 ++++++++++++++++++++++++++++++++ tests/Mocks/TestEncryptedJob.php | 25 ++++++++ 4 files changed, 129 insertions(+), 3 deletions(-) create mode 100644 tests/Mocks/TestEncryptedJob.php diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 71be4e98..d213fd63 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -10,6 +10,7 @@ + diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index 6cb2126f..ce63494d 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -7,8 +7,10 @@ use ErrorException; use Exception; use Illuminate\Contracts\Queue\Queue as QueueContract; +use Illuminate\Contracts\Queue\ShouldBeEncrypted; use Illuminate\Queue\Queue; use Illuminate\Support\Arr; +use Illuminate\Support\Facades\Crypt; use Illuminate\Support\Str; use PhpAmqpLib\Channel\AMQPChannel; use PhpAmqpLib\Connection\AbstractConnection; @@ -526,10 +528,10 @@ protected function createMessage($payload, int $attempts = 0): array if (isset($currentPayload['data']['command'])) { // If the command data is encrypted, decrypt it first before attempting to unserialize - if (in_array(ShouldBeEncrypted::class, class_implements($currentPayload['data']['commandName']))) { - $currentPayload['data']['command'] = decrypt($currentPayload['data']['command']); + if (is_subclass_of($currentPayload['data']['commandName'], ShouldBeEncrypted::class)) { + $currentPayload['data']['command'] = Crypt::decrypt($currentPayload['data']['command']); } - + $commandData = unserialize($currentPayload['data']['command']); if (property_exists($commandData, 'priority')) { $properties['priority'] = $commandData->priority; diff --git a/tests/Feature/TestCase.php b/tests/Feature/TestCase.php index c49d8608..16fd5faa 100644 --- a/tests/Feature/TestCase.php +++ b/tests/Feature/TestCase.php @@ -8,6 +8,7 @@ use PhpAmqpLib\Exception\AMQPProtocolChannelException; use RuntimeException; use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Jobs\RabbitMQJob; +use VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Mocks\TestEncryptedJob; use VladimirYuldashev\LaravelQueueRabbitMQ\Tests\Mocks\TestJob; use VladimirYuldashev\LaravelQueueRabbitMQ\Tests\TestCase as BaseTestCase; @@ -194,6 +195,103 @@ public function testBulk(): void $this->assertSame($count, Queue::size()); } + public function testPushEncrypted(): void + { + Queue::push(new TestEncryptedJob()); + + sleep(1); + + $this->assertSame(1, Queue::size()); + $this->assertNotNull($job = Queue::pop()); + $this->assertSame(1, $job->attempts()); + $this->assertInstanceOf(RabbitMQJob::class, $job); + $this->assertSame(TestEncryptedJob::class, $job->resolveName()); + $this->assertNotNull($job->getJobId()); + + $payload = $job->payload(); + + $this->assertSame(TestEncryptedJob::class, $payload['displayName']); + $this->assertSame('Illuminate\Queue\CallQueuedHandler@call', $payload['job']); + $this->assertNull($payload['maxTries']); + $this->assertNull($payload['backoff']); + $this->assertNull($payload['timeout']); + $this->assertNull($payload['retryUntil']); + $this->assertSame($job->getJobId(), $payload['id']); + + $job->delete(); + $this->assertSame(0, Queue::size()); + } + + public function testPushEncryptedAfterCommit(): void + { + $transaction = new DatabaseTransactionsManager; + + $this->app->singleton('db.transactions', function ($app) use ($transaction) { + $transaction->begin('FakeDBConnection', 1); + + return $transaction; + }); + + TestEncryptedJob::dispatch()->afterCommit(); + + sleep(1); + $this->assertSame(0, Queue::size()); + $this->assertNull(Queue::pop()); + + $transaction->commit('FakeDBConnection', 1, 0); + + sleep(1); + + $this->assertSame(1, Queue::size()); + $this->assertNotNull($job = Queue::pop()); + + $job->delete(); + $this->assertSame(0, Queue::size()); + } + + public function testEncryptedLater(): void + { + Queue::later(3, new TestEncryptedJob()); + + sleep(1); + + $this->assertSame(0, Queue::size()); + $this->assertNull(Queue::pop()); + + sleep(3); + + $this->assertSame(1, Queue::size()); + $this->assertNotNull($job = Queue::pop()); + + $this->assertInstanceOf(RabbitMQJob::class, $job); + + $body = json_decode($job->getRawBody(), true); + + $this->assertSame(TestEncryptedJob::class, $body['displayName']); + $this->assertSame('Illuminate\Queue\CallQueuedHandler@call', $body['job']); + $this->assertSame(TestEncryptedJob::class, $body['data']['commandName']); + $this->assertNotNull($job->getJobId()); + + $job->delete(); + $this->assertSame(0, Queue::size()); + } + + public function testEncryptedBulk(): void + { + $count = 100; + $jobs = []; + + for ($i = 0; $i < $count; $i++) { + $jobs[$i] = new TestEncryptedJob($i); + } + + Queue::bulk($jobs); + + sleep(1); + + $this->assertSame($count, Queue::size()); + } + public function testReleaseRaw(): void { Queue::pushRaw($payload = Str::random()); diff --git a/tests/Mocks/TestEncryptedJob.php b/tests/Mocks/TestEncryptedJob.php new file mode 100644 index 00000000..1c0e1762 --- /dev/null +++ b/tests/Mocks/TestEncryptedJob.php @@ -0,0 +1,25 @@ +i = $i; + } + + public function handle(): void + { + // + } +} From 813086aa9bb6e56fef09219e43eba481f9f3f66e Mon Sep 17 00:00:00 2001 From: Fabien Villepinte Date: Mon, 8 Jul 2024 09:13:26 +0200 Subject: [PATCH 367/375] Document the exceptions thrown by publishBasic() --- src/Queue/RabbitMQQueue.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index ce63494d..957620ef 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -15,6 +15,7 @@ use PhpAmqpLib\Channel\AMQPChannel; use PhpAmqpLib\Connection\AbstractConnection; use PhpAmqpLib\Exception\AMQPChannelClosedException; +use PhpAmqpLib\Exception\AMQPConnectionBlockedException; use PhpAmqpLib\Exception\AMQPConnectionClosedException; use PhpAmqpLib\Exception\AMQPProtocolChannelException; use PhpAmqpLib\Exception\AMQPRuntimeException; @@ -739,6 +740,11 @@ protected function getConfig(): QueueConfig return $this->config; } + /** + * @throws AMQPChannelClosedException + * @throws AMQPConnectionClosedException + * @throws AMQPConnectionBlockedException + */ protected function publishBasic($msg, $exchange = '', $destination = '', $mandatory = false, $immediate = false, $ticket = null): void { $this->getChannel()->basic_publish($msg, $exchange, $destination, $mandatory, $immediate, $ticket); From aaad79f2c7dd0a918de8b43dfe6ee2f117d6da94 Mon Sep 17 00:00:00 2001 From: VojtaB Date: Thu, 12 Dec 2024 19:47:42 +0100 Subject: [PATCH 368/375] load timeouts from laravel config --- README.md | 27 ++++++++++++++++++++++++++ src/Queue/Connection/ConfigFactory.php | 24 +++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/README.md b/README.md index adb7b2e9..30a10df9 100644 --- a/README.md +++ b/README.md @@ -529,6 +529,33 @@ Available protocols : `tcp`, `ssl`, `tls` ], ``` +### Network Timeouts + +For network timeouts configuration you can use option parameters. +All float values are in seconds and zero value can mean infinite timeout. +Example contains default values. + +```php +'connections' => [ + // ... + + 'rabbitmq' => [ + // ... + + 'options' => [ + // ... + + 'connection_timeout' => 3.0, + 'read_timeout' => 3.0, + 'write_timeout' => 3.0, + 'channel_rpc_timeout' => 0.0, + ], + ], + + // ... +], +``` + ### Octane support Starting with 13.3.0, this package supports [Laravel Octane](https://laravel.com/docs/octane) out of the box. diff --git a/src/Queue/Connection/ConfigFactory.php b/src/Queue/Connection/ConfigFactory.php index 7783b9cd..8e013efd 100644 --- a/src/Queue/Connection/ConfigFactory.php +++ b/src/Queue/Connection/ConfigFactory.php @@ -38,6 +38,7 @@ public static function make(array $config = []): AMQPConnectionConfig self::getHostFromConfig($connectionConfig, $config); self::getHeartbeatFromConfig($connectionConfig, $config); self::getNetworkProtocolFromConfig($connectionConfig, $config); + self::getTimeoutsFromConfig($connectionConfig, $config); }); } @@ -99,4 +100,27 @@ protected static function getNetworkProtocolFromConfig(AMQPConnectionConfig $con $connectionConfig->setNetworkProtocol($networkProtocol); } } + + protected static function getTimeoutsFromConfig(AMQPConnectionConfig $connectionConfig, array $config): void + { + $connectionTimeout = Arr::get($config, self::CONFIG_OPTIONS.'.connection_timeout'); + if (is_numeric($connectionTimeout) && floatval($connectionTimeout) >= 0) { + $connectionConfig->setConnectionTimeout((float) $connectionTimeout); + } + + $readTimeout = Arr::get($config, self::CONFIG_OPTIONS.'.read_timeout'); + if (is_numeric($readTimeout) && floatval($readTimeout) >= 0) { + $connectionConfig->setReadTimeout((float) $readTimeout); + } + + $writeTimeout = Arr::get($config, self::CONFIG_OPTIONS.'.write_timeout'); + if (is_numeric($writeTimeout) && floatval($writeTimeout) >= 0) { + $connectionConfig->setWriteTimeout((float) $writeTimeout); + } + + $chanelRpcTimeout = Arr::get($config, self::CONFIG_OPTIONS.'.channel_rpc_timeout'); + if (is_numeric($chanelRpcTimeout) && floatval($chanelRpcTimeout) >= 0) { + $connectionConfig->setChannelRPCTimeout((float) $chanelRpcTimeout); + } + } } From c00a8cce370a1525cacfff2b7c5e11d666943e5a Mon Sep 17 00:00:00 2001 From: Sergey Tarasenko Date: Thu, 23 Jan 2025 00:19:27 +0300 Subject: [PATCH 369/375] pint fixes --- pint.json | 4 ++-- src/Horizon/RabbitMQQueue.php | 2 +- src/Queue/Connection/ConfigFactory.php | 2 +- src/Queue/QueueConfigFactory.php | 2 +- src/Queue/RabbitMQQueue.php | 14 +++++++------- tests/Feature/QueueTest.php | 6 +++--- tests/Feature/SslQueueTest.php | 2 +- tests/Feature/TestCase.php | 20 ++++++++++---------- tests/Functional/RabbitMQQueueTest.php | 2 +- tests/TestCase.php | 2 +- 10 files changed, 28 insertions(+), 28 deletions(-) diff --git a/pint.json b/pint.json index 751c2203..029010c0 100644 --- a/pint.json +++ b/pint.json @@ -1,8 +1,8 @@ { "preset": "laravel", "rules": { - "nullable_type_declaration_for_default_null_value": { - "use_nullable_type_declaration": false + "php_unit_method_casing": { + "case": "camel_case" } } } diff --git a/src/Horizon/RabbitMQQueue.php b/src/Horizon/RabbitMQQueue.php index 894cc24b..e2ca400d 100644 --- a/src/Horizon/RabbitMQQueue.php +++ b/src/Horizon/RabbitMQQueue.php @@ -26,7 +26,7 @@ class RabbitMQQueue extends BaseRabbitMQQueue * * @throws AMQPProtocolChannelException */ - public function readyNow(string $queue = null): int + public function readyNow(?string $queue = null): int { return $this->size($queue); } diff --git a/src/Queue/Connection/ConfigFactory.php b/src/Queue/Connection/ConfigFactory.php index 7783b9cd..f3ecd409 100644 --- a/src/Queue/Connection/ConfigFactory.php +++ b/src/Queue/Connection/ConfigFactory.php @@ -16,7 +16,7 @@ class ConfigFactory */ public static function make(array $config = []): AMQPConnectionConfig { - return tap(new AMQPConnectionConfig(), function (AMQPConnectionConfig $connectionConfig) use ($config) { + return tap(new AMQPConnectionConfig, function (AMQPConnectionConfig $connectionConfig) use ($config) { // Set the connection to a Lazy by default $connectionConfig->setIsLazy(! in_array( Arr::get($config, 'lazy') ?? true, diff --git a/src/Queue/QueueConfigFactory.php b/src/Queue/QueueConfigFactory.php index 87fc2fac..6f2befc5 100644 --- a/src/Queue/QueueConfigFactory.php +++ b/src/Queue/QueueConfigFactory.php @@ -13,7 +13,7 @@ class QueueConfigFactory */ public static function make(array $config = []): QueueConfig { - return tap(new QueueConfig(), function (QueueConfig $queueConfig) use ($config) { + return tap(new QueueConfig, function (QueueConfig $queueConfig) use ($config) { if (! empty($queue = Arr::get($config, 'queue'))) { $queueConfig->setQueue($queue); } diff --git a/src/Queue/RabbitMQQueue.php b/src/Queue/RabbitMQQueue.php index 957620ef..fadedce5 100644 --- a/src/Queue/RabbitMQQueue.php +++ b/src/Queue/RabbitMQQueue.php @@ -204,7 +204,7 @@ protected function publishBatch($jobs, $data = '', $queue = null): void /** * @throws AMQPProtocolChannelException */ - public function bulkRaw(string $payload, string $queue = null, array $options = []): int|string|null + public function bulkRaw(string $payload, ?string $queue = null, array $options = []): int|string|null { [$destination, $exchange, $exchangeType, $attempts] = $this->publishProperties($queue, $options); @@ -397,7 +397,7 @@ public function deleteExchange(string $name, bool $unused = false): void * * @throws AMQPProtocolChannelException */ - public function isQueueExists(string $name = null): bool + public function isQueueExists(?string $name = null): bool { $queueName = $this->getQueue($name); @@ -484,7 +484,7 @@ public function bindQueue(string $queue, string $exchange, string $routingKey = /** * Purge the queue of messages. */ - public function purge(string $queue = null): void + public function purge(?string $queue = null): void { // create a temporary channel, so the main channel will not be closed on exception $channel = $this->createChannel(); @@ -637,7 +637,7 @@ protected function getDelayQueueArguments(string $destination, int $ttl): array /** * Get the exchange name, or empty string; as default value. */ - protected function getExchange(string $exchange = null): string + protected function getExchange(?string $exchange = null): string { return $exchange ?? $this->getConfig()->getExchange(); } @@ -654,7 +654,7 @@ protected function getRoutingKey(string $destination): string /** * Get the exchangeType, or AMQPExchangeType::DIRECT as default. */ - protected function getExchangeType(string $type = null): string + protected function getExchangeType(?string $type = null): string { $constant = AMQPExchangeType::class.'::'.Str::upper($type ?: $this->getConfig()->getExchangeType()); @@ -664,7 +664,7 @@ protected function getExchangeType(string $type = null): string /** * Get the exchange for failed messages. */ - protected function getFailedExchange(string $exchange = null): string + protected function getFailedExchange(?string $exchange = null): string { return $exchange ?? $this->getConfig()->getFailedExchange(); } @@ -699,7 +699,7 @@ protected function isQueueDeclared(string $name): bool * * @throws AMQPProtocolChannelException */ - protected function declareDestination(string $destination, string $exchange = null, string $exchangeType = AMQPExchangeType::DIRECT): void + protected function declareDestination(string $destination, ?string $exchange = null, string $exchangeType = AMQPExchangeType::DIRECT): void { // When an exchange is provided and no exchange is present in RabbitMQ, create an exchange. if ($exchange && ! $this->isExchangeExists($exchange)) { diff --git a/tests/Feature/QueueTest.php b/tests/Feature/QueueTest.php index 5ecfb978..ee324c9a 100644 --- a/tests/Feature/QueueTest.php +++ b/tests/Feature/QueueTest.php @@ -10,7 +10,7 @@ class QueueTest extends TestCase { - public function setUp(): void + protected function setUp(): void { parent::setUp(); @@ -29,7 +29,7 @@ public function testWithoutReconnect(): void { $queue = $this->connection('rabbitmq'); - $queue->push(new TestJob()); + $queue->push(new TestJob); sleep(1); $this->assertSame(1, $queue->size()); @@ -38,6 +38,6 @@ public function testWithoutReconnect(): void $this->assertFalse($queue->getConnection()->isConnected()); $this->expectException(AMQPChannelClosedException::class); - $queue->push(new TestJob()); + $queue->push(new TestJob); } } diff --git a/tests/Feature/SslQueueTest.php b/tests/Feature/SslQueueTest.php index e9c460ea..02181c96 100644 --- a/tests/Feature/SslQueueTest.php +++ b/tests/Feature/SslQueueTest.php @@ -6,7 +6,7 @@ class SslQueueTest extends TestCase { - public function setUp(): void + protected function setUp(): void { $this->markTestSkipped(); } diff --git a/tests/Feature/TestCase.php b/tests/Feature/TestCase.php index 16fd5faa..5afda85d 100644 --- a/tests/Feature/TestCase.php +++ b/tests/Feature/TestCase.php @@ -17,7 +17,7 @@ abstract class TestCase extends BaseTestCase /** * @throws AMQPProtocolChannelException */ - public function setUp(): void + protected function setUp(): void { parent::setUp(); @@ -70,7 +70,7 @@ public function testPushRaw(): void public function testPush(): void { - Queue::push(new TestJob()); + Queue::push(new TestJob); sleep(1); @@ -154,7 +154,7 @@ public function testLaterRaw(): void public function testLater(): void { - Queue::later(3, new TestJob()); + Queue::later(3, new TestJob); sleep(1); @@ -197,7 +197,7 @@ public function testBulk(): void public function testPushEncrypted(): void { - Queue::push(new TestEncryptedJob()); + Queue::push(new TestEncryptedJob); sleep(1); @@ -251,7 +251,7 @@ public function testPushEncryptedAfterCommit(): void public function testEncryptedLater(): void { - Queue::later(3, new TestEncryptedJob()); + Queue::later(3, new TestEncryptedJob); sleep(1); @@ -320,7 +320,7 @@ public function testReleaseRaw(): void public function testRelease(): void { - Queue::push(new TestJob()); + Queue::push(new TestJob); sleep(1); @@ -377,7 +377,7 @@ public function testReleaseWithDelayRaw(): void public function testReleaseInThePast(): void { - Queue::push(new TestJob()); + Queue::push(new TestJob); $job = Queue::pop(); $job->release(-3); @@ -392,7 +392,7 @@ public function testReleaseInThePast(): void public function testReleaseAndReleaseWithDelayAttempts(): void { - Queue::push(new TestJob()); + Queue::push(new TestJob); sleep(1); @@ -419,7 +419,7 @@ public function testReleaseAndReleaseWithDelayAttempts(): void public function testDelete(): void { - Queue::push(new TestJob()); + Queue::push(new TestJob); $job = Queue::pop(); @@ -433,7 +433,7 @@ public function testDelete(): void public function testFailed(): void { - Queue::push(new TestJob()); + Queue::push(new TestJob); $job = Queue::pop(); diff --git a/tests/Functional/RabbitMQQueueTest.php b/tests/Functional/RabbitMQQueueTest.php index 7a106a08..1c5d94fb 100644 --- a/tests/Functional/RabbitMQQueueTest.php +++ b/tests/Functional/RabbitMQQueueTest.php @@ -87,7 +87,7 @@ public function testConfigExchangeType(): void $queue = $this->connection('rabbitmq-with-options-null'); $this->assertSame(AMQPExchangeType::DIRECT, $this->callMethod($queue, 'getExchangeType')); - //testing an unkown type with a default + // testing an unkown type with a default $this->callProperty($queue, 'config')->setExchangeType('unknown'); $this->assertSame(AMQPExchangeType::DIRECT, $this->callMethod($queue, 'getExchangeType')); } diff --git a/tests/TestCase.php b/tests/TestCase.php index 7d50fa67..f1ffb8d4 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -50,7 +50,7 @@ protected function getEnvironmentSetUp($app): void ]); } - protected function connection(string $name = null): RabbitMQQueue + protected function connection(?string $name = null): RabbitMQQueue { return Queue::connection($name); } From 4765c204d3c86aef9d6e1eda75a413c38efdad36 Mon Sep 17 00:00:00 2001 From: Samuel Fontebasso Date: Sun, 20 Apr 2025 01:45:50 -0300 Subject: [PATCH 370/375] style: fix formatting issues to pass lint/tests --- src/Queue/Connection/ConfigFactory.php | 2 +- src/Queue/QueueConfigFactory.php | 2 +- tests/Feature/ConnectorTest.php | 8 ++-- tests/Feature/QueueTest.php | 10 ++--- tests/Feature/SslQueueTest.php | 4 +- tests/Feature/TestCase.php | 58 +++++++++++++------------- tests/Functional/RabbitMQQueueTest.php | 30 ++++++------- tests/Functional/TestCase.php | 4 +- 8 files changed, 59 insertions(+), 59 deletions(-) diff --git a/src/Queue/Connection/ConfigFactory.php b/src/Queue/Connection/ConfigFactory.php index 7783b9cd..f3ecd409 100644 --- a/src/Queue/Connection/ConfigFactory.php +++ b/src/Queue/Connection/ConfigFactory.php @@ -16,7 +16,7 @@ class ConfigFactory */ public static function make(array $config = []): AMQPConnectionConfig { - return tap(new AMQPConnectionConfig(), function (AMQPConnectionConfig $connectionConfig) use ($config) { + return tap(new AMQPConnectionConfig, function (AMQPConnectionConfig $connectionConfig) use ($config) { // Set the connection to a Lazy by default $connectionConfig->setIsLazy(! in_array( Arr::get($config, 'lazy') ?? true, diff --git a/src/Queue/QueueConfigFactory.php b/src/Queue/QueueConfigFactory.php index 87fc2fac..6f2befc5 100644 --- a/src/Queue/QueueConfigFactory.php +++ b/src/Queue/QueueConfigFactory.php @@ -13,7 +13,7 @@ class QueueConfigFactory */ public static function make(array $config = []): QueueConfig { - return tap(new QueueConfig(), function (QueueConfig $queueConfig) use ($config) { + return tap(new QueueConfig, function (QueueConfig $queueConfig) use ($config) { if (! empty($queue = Arr::get($config, 'queue'))) { $queueConfig->setQueue($queue); } diff --git a/tests/Feature/ConnectorTest.php b/tests/Feature/ConnectorTest.php index 91660ad2..f4e330c3 100644 --- a/tests/Feature/ConnectorTest.php +++ b/tests/Feature/ConnectorTest.php @@ -12,7 +12,7 @@ class ConnectorTest extends \VladimirYuldashev\LaravelQueueRabbitMQ\Tests\TestCase { - public function testLazyConnection(): void + public function test_lazy_connection(): void { $this->app['config']->set('queue.connections.rabbitmq', [ 'driver' => 'rabbitmq', @@ -55,7 +55,7 @@ public function testLazyConnection(): void $this->assertTrue($connection->getConnection()->isConnected()); } - public function testLazyStreamConnection(): void + public function test_lazy_stream_connection(): void { $this->app['config']->set('queue.connections.rabbitmq', [ 'driver' => 'rabbitmq', @@ -98,7 +98,7 @@ public function testLazyStreamConnection(): void $this->assertTrue($connection->getConnection()->isConnected()); } - public function testSslConnection(): void + public function test_ssl_connection(): void { $this->markTestSkipped(); @@ -142,7 +142,7 @@ public function testSslConnection(): void } // Test to validate ssl connection params - public function testNoVerificationSslConnection(): void + public function test_no_verification_ssl_connection(): void { $this->app['config']->set('queue.connections.rabbitmq', [ 'driver' => 'rabbitmq', diff --git a/tests/Feature/QueueTest.php b/tests/Feature/QueueTest.php index 5ecfb978..609a48d8 100644 --- a/tests/Feature/QueueTest.php +++ b/tests/Feature/QueueTest.php @@ -10,7 +10,7 @@ class QueueTest extends TestCase { - public function setUp(): void + protected function setUp(): void { parent::setUp(); @@ -20,16 +20,16 @@ public function setUp(): void ]); } - public function testConnection(): void + public function test_connection(): void { $this->assertInstanceOf(AMQPStreamConnection::class, $this->connection()->getChannel()->getConnection()); } - public function testWithoutReconnect(): void + public function test_without_reconnect(): void { $queue = $this->connection('rabbitmq'); - $queue->push(new TestJob()); + $queue->push(new TestJob); sleep(1); $this->assertSame(1, $queue->size()); @@ -38,6 +38,6 @@ public function testWithoutReconnect(): void $this->assertFalse($queue->getConnection()->isConnected()); $this->expectException(AMQPChannelClosedException::class); - $queue->push(new TestJob()); + $queue->push(new TestJob); } } diff --git a/tests/Feature/SslQueueTest.php b/tests/Feature/SslQueueTest.php index e9c460ea..53cf5d38 100644 --- a/tests/Feature/SslQueueTest.php +++ b/tests/Feature/SslQueueTest.php @@ -6,7 +6,7 @@ class SslQueueTest extends TestCase { - public function setUp(): void + protected function setUp(): void { $this->markTestSkipped(); } @@ -43,7 +43,7 @@ protected function getEnvironmentSetUp($app): void ]); } - public function testConnection(): void + public function test_connection(): void { $this->assertInstanceOf(AMQPSSLConnection::class, $this->connection()->getChannel()->getConnection()); } diff --git a/tests/Feature/TestCase.php b/tests/Feature/TestCase.php index 16fd5faa..7d0fab9a 100644 --- a/tests/Feature/TestCase.php +++ b/tests/Feature/TestCase.php @@ -17,7 +17,7 @@ abstract class TestCase extends BaseTestCase /** * @throws AMQPProtocolChannelException */ - public function setUp(): void + protected function setUp(): void { parent::setUp(); @@ -40,17 +40,17 @@ protected function tearDown(): void parent::tearDown(); } - public function testSizeDoesNotThrowExceptionOnUnknownQueue(): void + public function test_size_does_not_throw_exception_on_unknown_queue(): void { $this->assertEmpty(0, Queue::size(Str::random())); } - public function testPopNothing(): void + public function test_pop_nothing(): void { $this->assertNull(Queue::pop('foo')); } - public function testPushRaw(): void + public function test_push_raw(): void { Queue::pushRaw($payload = Str::random()); @@ -68,9 +68,9 @@ public function testPushRaw(): void $this->assertSame(0, Queue::size()); } - public function testPush(): void + public function test_push(): void { - Queue::push(new TestJob()); + Queue::push(new TestJob); sleep(1); @@ -95,7 +95,7 @@ public function testPush(): void $this->assertSame(0, Queue::size()); } - public function testPushAfterCommit(): void + public function test_push_after_commit(): void { $transaction = new DatabaseTransactionsManager; @@ -122,7 +122,7 @@ public function testPushAfterCommit(): void $this->assertSame(0, Queue::size()); } - public function testLaterRaw(): void + public function test_later_raw(): void { $payload = Str::random(); $data = [Str::random() => Str::random()]; @@ -152,9 +152,9 @@ public function testLaterRaw(): void $this->assertSame(0, Queue::size()); } - public function testLater(): void + public function test_later(): void { - Queue::later(3, new TestJob()); + Queue::later(3, new TestJob); sleep(1); @@ -179,7 +179,7 @@ public function testLater(): void $this->assertSame(0, Queue::size()); } - public function testBulk(): void + public function test_bulk(): void { $count = 100; $jobs = []; @@ -195,9 +195,9 @@ public function testBulk(): void $this->assertSame($count, Queue::size()); } - public function testPushEncrypted(): void + public function test_push_encrypted(): void { - Queue::push(new TestEncryptedJob()); + Queue::push(new TestEncryptedJob); sleep(1); @@ -222,7 +222,7 @@ public function testPushEncrypted(): void $this->assertSame(0, Queue::size()); } - public function testPushEncryptedAfterCommit(): void + public function test_push_encrypted_after_commit(): void { $transaction = new DatabaseTransactionsManager; @@ -249,9 +249,9 @@ public function testPushEncryptedAfterCommit(): void $this->assertSame(0, Queue::size()); } - public function testEncryptedLater(): void + public function test_encrypted_later(): void { - Queue::later(3, new TestEncryptedJob()); + Queue::later(3, new TestEncryptedJob); sleep(1); @@ -276,7 +276,7 @@ public function testEncryptedLater(): void $this->assertSame(0, Queue::size()); } - public function testEncryptedBulk(): void + public function test_encrypted_bulk(): void { $count = 100; $jobs = []; @@ -292,7 +292,7 @@ public function testEncryptedBulk(): void $this->assertSame($count, Queue::size()); } - public function testReleaseRaw(): void + public function test_release_raw(): void { Queue::pushRaw($payload = Str::random()); @@ -318,9 +318,9 @@ public function testReleaseRaw(): void $this->assertSame(0, Queue::size()); } - public function testRelease(): void + public function test_release(): void { - Queue::push(new TestJob()); + Queue::push(new TestJob); sleep(1); @@ -344,7 +344,7 @@ public function testRelease(): void $this->assertSame(0, Queue::size()); } - public function testReleaseWithDelayRaw(): void + public function test_release_with_delay_raw(): void { Queue::pushRaw($payload = Str::random()); @@ -375,9 +375,9 @@ public function testReleaseWithDelayRaw(): void $this->assertSame(0, Queue::size()); } - public function testReleaseInThePast(): void + public function test_release_in_the_past(): void { - Queue::push(new TestJob()); + Queue::push(new TestJob); $job = Queue::pop(); $job->release(-3); @@ -390,9 +390,9 @@ public function testReleaseInThePast(): void $this->assertSame(0, Queue::size()); } - public function testReleaseAndReleaseWithDelayAttempts(): void + public function test_release_and_release_with_delay_attempts(): void { - Queue::push(new TestJob()); + Queue::push(new TestJob); sleep(1); @@ -417,9 +417,9 @@ public function testReleaseAndReleaseWithDelayAttempts(): void $this->assertSame(0, Queue::size()); } - public function testDelete(): void + public function test_delete(): void { - Queue::push(new TestJob()); + Queue::push(new TestJob); $job = Queue::pop(); @@ -431,9 +431,9 @@ public function testDelete(): void $this->assertNull(Queue::pop()); } - public function testFailed(): void + public function test_failed(): void { - Queue::push(new TestJob()); + Queue::push(new TestJob); $job = Queue::pop(); diff --git a/tests/Functional/RabbitMQQueueTest.php b/tests/Functional/RabbitMQQueueTest.php index 7a106a08..21a2e1d4 100644 --- a/tests/Functional/RabbitMQQueueTest.php +++ b/tests/Functional/RabbitMQQueueTest.php @@ -9,7 +9,7 @@ class RabbitMQQueueTest extends BaseTestCase { - public function testConnection(): void + public function test_connection(): void { $queue = $this->connection(); $this->assertInstanceOf(RabbitMQQueue::class, $queue); @@ -21,7 +21,7 @@ public function testConnection(): void $this->assertInstanceOf(RabbitMQQueue::class, $queue); } - public function testConfigRerouteFailed(): void + public function test_config_reroute_failed(): void { $queue = $this->connection(); $this->assertFalse($this->callProperty($queue, 'config')->isRerouteFailed()); @@ -36,7 +36,7 @@ public function testConfigRerouteFailed(): void $this->assertFalse($this->callProperty($queue, 'config')->isRerouteFailed()); } - public function testConfigPrioritizeDelayed(): void + public function test_config_prioritize_delayed(): void { $queue = $this->connection(); $this->assertFalse($this->callProperty($queue, 'config')->isPrioritizeDelayed()); @@ -51,7 +51,7 @@ public function testConfigPrioritizeDelayed(): void $this->assertFalse($this->callProperty($queue, 'config')->isPrioritizeDelayed()); } - public function testQueueMaxPriority(): void + public function test_queue_max_priority(): void { $queue = $this->connection(); $this->assertIsInt($this->callProperty($queue, 'config')->getQueueMaxPriority()); @@ -70,7 +70,7 @@ public function testQueueMaxPriority(): void $this->assertSame(2, $this->callProperty($queue, 'config')->getQueueMaxPriority()); } - public function testConfigExchangeType(): void + public function test_config_exchange_type(): void { $queue = $this->connection(); $this->assertSame(AMQPExchangeType::DIRECT, $this->callMethod($queue, 'getExchangeType')); @@ -87,12 +87,12 @@ public function testConfigExchangeType(): void $queue = $this->connection('rabbitmq-with-options-null'); $this->assertSame(AMQPExchangeType::DIRECT, $this->callMethod($queue, 'getExchangeType')); - //testing an unkown type with a default + // testing an unkown type with a default $this->callProperty($queue, 'config')->setExchangeType('unknown'); $this->assertSame(AMQPExchangeType::DIRECT, $this->callMethod($queue, 'getExchangeType')); } - public function testExchange(): void + public function test_exchange(): void { $queue = $this->connection(); $this->assertSame('test', $this->callMethod($queue, 'getExchange', ['test'])); @@ -119,7 +119,7 @@ public function testExchange(): void $this->assertSame('', $this->callMethod($queue, 'getExchange', [''])); } - public function testFailedExchange(): void + public function test_failed_exchange(): void { $queue = $this->connection(); $this->assertSame('test', $this->callMethod($queue, 'getFailedExchange', ['test'])); @@ -146,7 +146,7 @@ public function testFailedExchange(): void $this->assertSame('', $this->callMethod($queue, 'getFailedExchange', [''])); } - public function testRoutingKey(): void + public function test_routing_key(): void { $queue = $this->connection(); $this->assertSame('test', $this->callMethod($queue, 'getRoutingKey', ['test'])); @@ -165,7 +165,7 @@ public function testRoutingKey(): void $this->assertSame('an.alternate.routing-key', $this->callMethod($queue, 'getRoutingKey', ['test'])); } - public function testFailedRoutingKey(): void + public function test_failed_routing_key(): void { $queue = $this->connection(); $this->assertSame('test.failed', $this->callMethod($queue, 'getFailedRoutingKey', ['test'])); @@ -184,7 +184,7 @@ public function testFailedRoutingKey(): void $this->assertSame('an.alternate.routing-key', $this->callMethod($queue, 'getFailedRoutingKey', ['test'])); } - public function testConfigQuorum(): void + public function test_config_quorum(): void { $queue = $this->connection(); $this->assertFalse($this->callProperty($queue, 'config')->isQuorum()); @@ -202,7 +202,7 @@ public function testConfigQuorum(): void $this->assertTrue($this->callProperty($queue, 'config')->isQuorum()); } - public function testDeclareDeleteExchange(): void + public function test_declare_delete_exchange(): void { $queue = $this->connection(); @@ -217,7 +217,7 @@ public function testDeclareDeleteExchange(): void $this->assertFalse($queue->isExchangeExists($name)); } - public function testDeclareDeleteQueue(): void + public function test_declare_delete_queue(): void { $queue = $this->connection(); @@ -232,7 +232,7 @@ public function testDeclareDeleteQueue(): void $this->assertFalse($queue->isQueueExists($name)); } - public function testQueueArguments(): void + public function test_queue_arguments(): void { $name = Str::random(); @@ -272,7 +272,7 @@ public function testQueueArguments(): void $this->assertEquals(array_values($expected), array_values($actual)); } - public function testDelayQueueArguments(): void + public function test_delay_queue_arguments(): void { $name = Str::random(); $ttl = 12000; diff --git a/tests/Functional/TestCase.php b/tests/Functional/TestCase.php index 8b843561..b21aeaf8 100644 --- a/tests/Functional/TestCase.php +++ b/tests/Functional/TestCase.php @@ -236,7 +236,7 @@ protected function callProperty($object, string $property): mixed return $property->getValue($object); } - public function testConnectChannel(): void + public function test_connect_channel(): void { $queue = $this->connection(); $this->assertFalse($queue->getConnection()->isConnected()); @@ -248,7 +248,7 @@ public function testConnectChannel(): void $this->assertTrue($channel->is_open()); } - public function testReconnect(): void + public function test_reconnect(): void { $queue = $this->connection(); $this->assertFalse($queue->getConnection()->isConnected()); From 9c5fa17a0aa0d175d4849703553997b6edb72613 Mon Sep 17 00:00:00 2001 From: Samuel Fontebasso Date: Sun, 20 Apr 2025 02:01:38 -0300 Subject: [PATCH 371/375] fix: remove manual docker-compose v1 install to prevent CI failure on ubuntu-latest --- .github/workflows/tests.yml | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index fccb0608..0da38f44 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -33,15 +33,8 @@ jobs: extensions: dom, curl, libxml, mbstring, zip coverage: none - - name: Set up Docker - run: | - sudo rm /usr/local/bin/docker-compose - curl -L https://github.com/docker/compose/releases/download/1.24.1/docker-compose-`uname -s`-`uname -m` > docker-compose - chmod +x docker-compose - sudo mv docker-compose /usr/local/bin - - name: Start Docker container - run: docker-compose up -d rabbitmq + run: docker compose up -d rabbitmq - name: Install dependencies run: composer update --with='laravel/framework:${{matrix.laravel}}' --${{ matrix.stability }} --prefer-dist --no-interaction --no-progress From e69dd13ebfde40ae6993ee977e67ca22bd3e9609 Mon Sep 17 00:00:00 2001 From: Samuel Fontebasso Date: Mon, 21 Apr 2025 15:28:51 -0300 Subject: [PATCH 372/375] feat: add Laravel 12 support and fix test connection by disabling SSL verify_peer --- .github/workflows/tests.yml | 4 +++- composer.json | 6 +++--- tests/TestCase.php | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 0da38f44..d0588c10 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -15,10 +15,12 @@ jobs: matrix: php: ['8.1', '8.2', '8.3'] stability: ['prefer-lowest', 'prefer-stable'] - laravel: ['^10.0', '^11.0'] + laravel: ['^10.0', '^11.0', '^12.0'] exclude: - php: '8.1' laravel: '^11.0' + - php: '8.1' + laravel: '^12.0' name: 'PHP ${{ matrix.php }} - Laravel: ${{matrix.laravel}} - ${{ matrix.stability }}' diff --git a/composer.json b/composer.json index 1a91d725..caf8a0fc 100644 --- a/composer.json +++ b/composer.json @@ -11,16 +11,16 @@ "require": { "php": "^8.0", "ext-json": "*", - "illuminate/queue": "^10.0|^11.0", + "illuminate/queue": "^10.0|^11.0|^12.0", "php-amqplib/php-amqplib": "^v3.6" }, "require-dev": { "phpunit/phpunit": "^10.0|^11.0", "mockery/mockery": "^1.0", "laravel/horizon": "^5.0", - "orchestra/testbench": "^7.0|^8.0|^9.0", + "orchestra/testbench": "^7.0|^8.0|^9.0|^10.0", "laravel/pint": "^1.2", - "laravel/framework": "^9.0|^10.0|^11.0" + "laravel/framework": "^9.0|^10.0|^11.0|^12.0" }, "autoload": { "psr-4": { diff --git a/tests/TestCase.php b/tests/TestCase.php index 7d50fa67..f3f0705d 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -40,7 +40,7 @@ protected function getEnvironmentSetUp($app): void 'cafile' => null, 'local_cert' => null, 'local_key' => null, - 'verify_peer' => true, + 'verify_peer' => false, 'passphrase' => null, ], ], From 6faa83ac6554a0b8340756c7ffee0f7b68741cd7 Mon Sep 17 00:00:00 2001 From: Samuel Fontebasso Date: Mon, 21 Apr 2025 17:02:52 -0300 Subject: [PATCH 373/375] feat: add --json flag to rabbitmq:consume and align option order with WorkCommand --- src/Console/ConsumeCommand.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Console/ConsumeCommand.php b/src/Console/ConsumeCommand.php index 50aab1d4..4072132a 100644 --- a/src/Console/ConsumeCommand.php +++ b/src/Console/ConsumeCommand.php @@ -21,9 +21,10 @@ class ConsumeCommand extends WorkCommand {--force : Force the worker to run even in maintenance mode} {--memory=128 : The memory limit in megabytes} {--sleep=3 : Number of seconds to sleep when no job is available} + {--rest=0 : Number of seconds to rest between jobs} {--timeout=60 : The number of seconds a child process can run} {--tries=1 : Number of times to attempt a job before logging it failed} - {--rest=0 : Number of seconds to rest between jobs} + {--json : Output the queue worker information as JSON} {--max-priority=} {--consumer-tag} From 4717111a1e3f7e70cc0e43e7752a5720fdd86207 Mon Sep 17 00:00:00 2001 From: Samuel Fontebasso Date: Wed, 30 Apr 2025 21:58:45 -0300 Subject: [PATCH 374/375] chore(ci): add PHP 8.4 to test matrix --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index d0588c10..4aad6d20 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -13,7 +13,7 @@ jobs: strategy: fail-fast: true matrix: - php: ['8.1', '8.2', '8.3'] + php: ['8.1', '8.2', '8.3', '8.4'] stability: ['prefer-lowest', 'prefer-stable'] laravel: ['^10.0', '^11.0', '^12.0'] exclude: From e82de4ceef0ab3ddf5f4aaaa2816ea4c2dc49ef5 Mon Sep 17 00:00:00 2001 From: Samuel Fontebasso Date: Wed, 30 Apr 2025 22:24:44 -0300 Subject: [PATCH 375/375] style: rename PHPUnit test methods to camelCase --- pint.json | 2 +- tests/Feature/ConnectorTest.php | 8 +++--- tests/Feature/QueueTest.php | 4 +-- tests/Feature/SslQueueTest.php | 2 +- tests/Feature/TestCase.php | 38 +++++++++++++------------- tests/Functional/RabbitMQQueueTest.php | 28 +++++++++---------- tests/Functional/TestCase.php | 4 +-- 7 files changed, 43 insertions(+), 43 deletions(-) diff --git a/pint.json b/pint.json index 029010c0..05f4b41e 100644 --- a/pint.json +++ b/pint.json @@ -2,7 +2,7 @@ "preset": "laravel", "rules": { "php_unit_method_casing": { - "case": "camel_case" + "case": "camel_case" } } } diff --git a/tests/Feature/ConnectorTest.php b/tests/Feature/ConnectorTest.php index f4e330c3..91660ad2 100644 --- a/tests/Feature/ConnectorTest.php +++ b/tests/Feature/ConnectorTest.php @@ -12,7 +12,7 @@ class ConnectorTest extends \VladimirYuldashev\LaravelQueueRabbitMQ\Tests\TestCase { - public function test_lazy_connection(): void + public function testLazyConnection(): void { $this->app['config']->set('queue.connections.rabbitmq', [ 'driver' => 'rabbitmq', @@ -55,7 +55,7 @@ public function test_lazy_connection(): void $this->assertTrue($connection->getConnection()->isConnected()); } - public function test_lazy_stream_connection(): void + public function testLazyStreamConnection(): void { $this->app['config']->set('queue.connections.rabbitmq', [ 'driver' => 'rabbitmq', @@ -98,7 +98,7 @@ public function test_lazy_stream_connection(): void $this->assertTrue($connection->getConnection()->isConnected()); } - public function test_ssl_connection(): void + public function testSslConnection(): void { $this->markTestSkipped(); @@ -142,7 +142,7 @@ public function test_ssl_connection(): void } // Test to validate ssl connection params - public function test_no_verification_ssl_connection(): void + public function testNoVerificationSslConnection(): void { $this->app['config']->set('queue.connections.rabbitmq', [ 'driver' => 'rabbitmq', diff --git a/tests/Feature/QueueTest.php b/tests/Feature/QueueTest.php index 609a48d8..ee324c9a 100644 --- a/tests/Feature/QueueTest.php +++ b/tests/Feature/QueueTest.php @@ -20,12 +20,12 @@ protected function setUp(): void ]); } - public function test_connection(): void + public function testConnection(): void { $this->assertInstanceOf(AMQPStreamConnection::class, $this->connection()->getChannel()->getConnection()); } - public function test_without_reconnect(): void + public function testWithoutReconnect(): void { $queue = $this->connection('rabbitmq'); diff --git a/tests/Feature/SslQueueTest.php b/tests/Feature/SslQueueTest.php index 53cf5d38..02181c96 100644 --- a/tests/Feature/SslQueueTest.php +++ b/tests/Feature/SslQueueTest.php @@ -43,7 +43,7 @@ protected function getEnvironmentSetUp($app): void ]); } - public function test_connection(): void + public function testConnection(): void { $this->assertInstanceOf(AMQPSSLConnection::class, $this->connection()->getChannel()->getConnection()); } diff --git a/tests/Feature/TestCase.php b/tests/Feature/TestCase.php index 7d0fab9a..5afda85d 100644 --- a/tests/Feature/TestCase.php +++ b/tests/Feature/TestCase.php @@ -40,17 +40,17 @@ protected function tearDown(): void parent::tearDown(); } - public function test_size_does_not_throw_exception_on_unknown_queue(): void + public function testSizeDoesNotThrowExceptionOnUnknownQueue(): void { $this->assertEmpty(0, Queue::size(Str::random())); } - public function test_pop_nothing(): void + public function testPopNothing(): void { $this->assertNull(Queue::pop('foo')); } - public function test_push_raw(): void + public function testPushRaw(): void { Queue::pushRaw($payload = Str::random()); @@ -68,7 +68,7 @@ public function test_push_raw(): void $this->assertSame(0, Queue::size()); } - public function test_push(): void + public function testPush(): void { Queue::push(new TestJob); @@ -95,7 +95,7 @@ public function test_push(): void $this->assertSame(0, Queue::size()); } - public function test_push_after_commit(): void + public function testPushAfterCommit(): void { $transaction = new DatabaseTransactionsManager; @@ -122,7 +122,7 @@ public function test_push_after_commit(): void $this->assertSame(0, Queue::size()); } - public function test_later_raw(): void + public function testLaterRaw(): void { $payload = Str::random(); $data = [Str::random() => Str::random()]; @@ -152,7 +152,7 @@ public function test_later_raw(): void $this->assertSame(0, Queue::size()); } - public function test_later(): void + public function testLater(): void { Queue::later(3, new TestJob); @@ -179,7 +179,7 @@ public function test_later(): void $this->assertSame(0, Queue::size()); } - public function test_bulk(): void + public function testBulk(): void { $count = 100; $jobs = []; @@ -195,7 +195,7 @@ public function test_bulk(): void $this->assertSame($count, Queue::size()); } - public function test_push_encrypted(): void + public function testPushEncrypted(): void { Queue::push(new TestEncryptedJob); @@ -222,7 +222,7 @@ public function test_push_encrypted(): void $this->assertSame(0, Queue::size()); } - public function test_push_encrypted_after_commit(): void + public function testPushEncryptedAfterCommit(): void { $transaction = new DatabaseTransactionsManager; @@ -249,7 +249,7 @@ public function test_push_encrypted_after_commit(): void $this->assertSame(0, Queue::size()); } - public function test_encrypted_later(): void + public function testEncryptedLater(): void { Queue::later(3, new TestEncryptedJob); @@ -276,7 +276,7 @@ public function test_encrypted_later(): void $this->assertSame(0, Queue::size()); } - public function test_encrypted_bulk(): void + public function testEncryptedBulk(): void { $count = 100; $jobs = []; @@ -292,7 +292,7 @@ public function test_encrypted_bulk(): void $this->assertSame($count, Queue::size()); } - public function test_release_raw(): void + public function testReleaseRaw(): void { Queue::pushRaw($payload = Str::random()); @@ -318,7 +318,7 @@ public function test_release_raw(): void $this->assertSame(0, Queue::size()); } - public function test_release(): void + public function testRelease(): void { Queue::push(new TestJob); @@ -344,7 +344,7 @@ public function test_release(): void $this->assertSame(0, Queue::size()); } - public function test_release_with_delay_raw(): void + public function testReleaseWithDelayRaw(): void { Queue::pushRaw($payload = Str::random()); @@ -375,7 +375,7 @@ public function test_release_with_delay_raw(): void $this->assertSame(0, Queue::size()); } - public function test_release_in_the_past(): void + public function testReleaseInThePast(): void { Queue::push(new TestJob); @@ -390,7 +390,7 @@ public function test_release_in_the_past(): void $this->assertSame(0, Queue::size()); } - public function test_release_and_release_with_delay_attempts(): void + public function testReleaseAndReleaseWithDelayAttempts(): void { Queue::push(new TestJob); @@ -417,7 +417,7 @@ public function test_release_and_release_with_delay_attempts(): void $this->assertSame(0, Queue::size()); } - public function test_delete(): void + public function testDelete(): void { Queue::push(new TestJob); @@ -431,7 +431,7 @@ public function test_delete(): void $this->assertNull(Queue::pop()); } - public function test_failed(): void + public function testFailed(): void { Queue::push(new TestJob); diff --git a/tests/Functional/RabbitMQQueueTest.php b/tests/Functional/RabbitMQQueueTest.php index 21a2e1d4..1c5d94fb 100644 --- a/tests/Functional/RabbitMQQueueTest.php +++ b/tests/Functional/RabbitMQQueueTest.php @@ -9,7 +9,7 @@ class RabbitMQQueueTest extends BaseTestCase { - public function test_connection(): void + public function testConnection(): void { $queue = $this->connection(); $this->assertInstanceOf(RabbitMQQueue::class, $queue); @@ -21,7 +21,7 @@ public function test_connection(): void $this->assertInstanceOf(RabbitMQQueue::class, $queue); } - public function test_config_reroute_failed(): void + public function testConfigRerouteFailed(): void { $queue = $this->connection(); $this->assertFalse($this->callProperty($queue, 'config')->isRerouteFailed()); @@ -36,7 +36,7 @@ public function test_config_reroute_failed(): void $this->assertFalse($this->callProperty($queue, 'config')->isRerouteFailed()); } - public function test_config_prioritize_delayed(): void + public function testConfigPrioritizeDelayed(): void { $queue = $this->connection(); $this->assertFalse($this->callProperty($queue, 'config')->isPrioritizeDelayed()); @@ -51,7 +51,7 @@ public function test_config_prioritize_delayed(): void $this->assertFalse($this->callProperty($queue, 'config')->isPrioritizeDelayed()); } - public function test_queue_max_priority(): void + public function testQueueMaxPriority(): void { $queue = $this->connection(); $this->assertIsInt($this->callProperty($queue, 'config')->getQueueMaxPriority()); @@ -70,7 +70,7 @@ public function test_queue_max_priority(): void $this->assertSame(2, $this->callProperty($queue, 'config')->getQueueMaxPriority()); } - public function test_config_exchange_type(): void + public function testConfigExchangeType(): void { $queue = $this->connection(); $this->assertSame(AMQPExchangeType::DIRECT, $this->callMethod($queue, 'getExchangeType')); @@ -92,7 +92,7 @@ public function test_config_exchange_type(): void $this->assertSame(AMQPExchangeType::DIRECT, $this->callMethod($queue, 'getExchangeType')); } - public function test_exchange(): void + public function testExchange(): void { $queue = $this->connection(); $this->assertSame('test', $this->callMethod($queue, 'getExchange', ['test'])); @@ -119,7 +119,7 @@ public function test_exchange(): void $this->assertSame('', $this->callMethod($queue, 'getExchange', [''])); } - public function test_failed_exchange(): void + public function testFailedExchange(): void { $queue = $this->connection(); $this->assertSame('test', $this->callMethod($queue, 'getFailedExchange', ['test'])); @@ -146,7 +146,7 @@ public function test_failed_exchange(): void $this->assertSame('', $this->callMethod($queue, 'getFailedExchange', [''])); } - public function test_routing_key(): void + public function testRoutingKey(): void { $queue = $this->connection(); $this->assertSame('test', $this->callMethod($queue, 'getRoutingKey', ['test'])); @@ -165,7 +165,7 @@ public function test_routing_key(): void $this->assertSame('an.alternate.routing-key', $this->callMethod($queue, 'getRoutingKey', ['test'])); } - public function test_failed_routing_key(): void + public function testFailedRoutingKey(): void { $queue = $this->connection(); $this->assertSame('test.failed', $this->callMethod($queue, 'getFailedRoutingKey', ['test'])); @@ -184,7 +184,7 @@ public function test_failed_routing_key(): void $this->assertSame('an.alternate.routing-key', $this->callMethod($queue, 'getFailedRoutingKey', ['test'])); } - public function test_config_quorum(): void + public function testConfigQuorum(): void { $queue = $this->connection(); $this->assertFalse($this->callProperty($queue, 'config')->isQuorum()); @@ -202,7 +202,7 @@ public function test_config_quorum(): void $this->assertTrue($this->callProperty($queue, 'config')->isQuorum()); } - public function test_declare_delete_exchange(): void + public function testDeclareDeleteExchange(): void { $queue = $this->connection(); @@ -217,7 +217,7 @@ public function test_declare_delete_exchange(): void $this->assertFalse($queue->isExchangeExists($name)); } - public function test_declare_delete_queue(): void + public function testDeclareDeleteQueue(): void { $queue = $this->connection(); @@ -232,7 +232,7 @@ public function test_declare_delete_queue(): void $this->assertFalse($queue->isQueueExists($name)); } - public function test_queue_arguments(): void + public function testQueueArguments(): void { $name = Str::random(); @@ -272,7 +272,7 @@ public function test_queue_arguments(): void $this->assertEquals(array_values($expected), array_values($actual)); } - public function test_delay_queue_arguments(): void + public function testDelayQueueArguments(): void { $name = Str::random(); $ttl = 12000; diff --git a/tests/Functional/TestCase.php b/tests/Functional/TestCase.php index b21aeaf8..8b843561 100644 --- a/tests/Functional/TestCase.php +++ b/tests/Functional/TestCase.php @@ -236,7 +236,7 @@ protected function callProperty($object, string $property): mixed return $property->getValue($object); } - public function test_connect_channel(): void + public function testConnectChannel(): void { $queue = $this->connection(); $this->assertFalse($queue->getConnection()->isConnected()); @@ -248,7 +248,7 @@ public function test_connect_channel(): void $this->assertTrue($channel->is_open()); } - public function test_reconnect(): void + public function testReconnect(): void { $queue = $this->connection(); $this->assertFalse($queue->getConnection()->isConnected());