Skip to content

Commit f351538

Browse files
Merge pull request #20 from run-as-root/develop
Configure retry through xml instead of core_config_data
2 parents 47271c0 + d55b990 commit f351538

26 files changed

+368
-434
lines changed

README.md

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,10 @@ Is possible to configure the ACL for each action in the grid and the module conf
9595

9696
Two steps are necessary to configure the retry for a queue:
9797
1. Configure the dead letter exchange
98-
2. Enable the message queue retry and declare the retry limit configuration
98+
1. Declare the retry limit xml configuration
99+
1. Enable the message queue retry admin configuration
100+
101+
#### 1. Configuring the dead letter exchange
99102

100103
Let's imagine a scenario that the `erp_order_export` queue already exists in your project and to simplify the example the topic name, exchange name and queue name are the same: `erp_order_export`.
101104

@@ -172,13 +175,29 @@ We added the `erp_order_export_delay` exchange and binding, it points to the ori
172175

173176
The `erp_order_export_delay` queue does not have a consumer, it will be used only to hold(delay) messages according with the period defined in the `x-message-ttl` argument.
174177

175-
Now you have to define toggle the activation for the retry queue module and declare the retry limit for the queue:
178+
#### 2. Declaring the retry limit xml configuration
179+
180+
Create the `Vendor_ModuleName/etc/queue_retry.xml` file with the content:
181+
182+
```xml
183+
<?xml version="1.0"?>
184+
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
185+
xsi:noNamespaceSchemaLocation="urn:RunAsRoot:module:RunAsRoot_MessageQueueRetry:/etc/queue_retry.xsd">
186+
<topic name="erp_order_export" retryLimit="3"/>
187+
</config>
188+
```
189+
190+
#### 3. Enabling the message queue retry admin configuration
191+
192+
Now you have to toggle the activation for the retry queue module:
176193

177194
System > Configuration > RUN-AS-ROOT > Message Queue Retry
178195

179-
![img.png](docs/configuration.png)
196+
![img.png](docs/module-configuration.png)
180197

181-
**Important note:** Make sure to configure the retry limit of your queue in the module configuration. If you configure the dead letter exchange and do not set the retry limit in the configuration(System > Configuration > RUN-AS-ROOT > Message Queue Retry), the message will be in a retry loop, that is, execute until the consumer process the message without throwing an exception. This is the default behavior for the RabbitMQ dead letter exchange and will work this way even if this module is not installed.
198+
**Important note:** Make sure to configure the retry limit of your queue with the `queue_retry.xml` file and enable the message queue retry configuration.
199+
If you configure the dead letter exchange and do not do the steps mentioned, the message will be in a retry loop. In other words, it will execute until the consumer processes the message without throwing an exception.
200+
This is the default behavior for the RabbitMQ dead letter exchange and will work this way even if this module is not installed.
182201

183202
For more information of how to configure message queues in Magento 2, you can take a look [here](https://developer.adobe.com/commerce/php/development/components/message-queues/configuration/).
184203

docs/configuration.png

-117 KB
Binary file not shown.

docs/module-configuration.png

76.3 KB
Loading

phpstan.neon.dist

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ parameters:
55
excludePaths:
66
analyseAndScan:
77
- src/Test
8-
ignoreErrors:
9-
- '#Method .*construct\(\) has parameter \$data with no value type specified in iterable type array#'
108

119
includes:
1210
- vendor/bitexpert/phpstan-magento/extension.neon

src/Block/Adminhtml/QueuesConfig.php

Lines changed: 0 additions & 27 deletions
This file was deleted.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace RunAsRoot\MessageQueueRetry\Config;
6+
7+
interface QueueRetryConfigInterface
8+
{
9+
public const CONFIG_KEY_NAME = 'queue_retry_topics';
10+
public const CACHE_KEY = 'queue_retry_config';
11+
public const FILE_NAME = 'queue_retry.xml';
12+
public const TOPIC_NAME = 'topic_name';
13+
public const RETRY_LIMIT = 'retry_limit';
14+
public const XSD_FILE_URN = 'urn:RunAsRoot:module:RunAsRoot_MessageQueueRetry:/etc/queue_retry.xsd';
15+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace RunAsRoot\MessageQueueRetry\Converter;
6+
7+
use Magento\Framework\Config\ConverterInterface;
8+
use RunAsRoot\MessageQueueRetry\Config\QueueRetryConfigInterface;
9+
10+
class QueueRetryXmlToArrayConverter implements ConverterInterface
11+
{
12+
/**
13+
* @return array<string, array<string, array<string,int|string|null>>>
14+
*/
15+
public function convert($source): array
16+
{
17+
$topics = [];
18+
19+
foreach ($source->getElementsByTagName('topic') as $topicNode) {
20+
$topicAttributes = $topicNode->attributes;
21+
$topicName = $topicAttributes->getNamedItem('name')?->nodeValue;
22+
$retryLimit = (int)$topicAttributes->getNamedItem('retryLimit')?->nodeValue;
23+
24+
$topics[$topicName] = [
25+
QueueRetryConfigInterface::TOPIC_NAME => $topicName,
26+
QueueRetryConfigInterface::RETRY_LIMIT => $retryLimit,
27+
];
28+
}
29+
30+
return [ QueueRetryConfigInterface::CONFIG_KEY_NAME => $topics ];
31+
}
32+
}

src/Model/Config/Backend/QueuesConfig.php

Lines changed: 0 additions & 59 deletions
This file was deleted.
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace RunAsRoot\MessageQueueRetry\Repository\Query;
6+
7+
use Magento\Framework\Config\DataInterface;
8+
use RunAsRoot\MessageQueueRetry\Config\QueueRetryConfigInterface;
9+
10+
class FindQueueRetryLimitByTopicNameQuery
11+
{
12+
public function __construct(private DataInterface $configStorage)
13+
{
14+
}
15+
16+
public function execute(string $topicName): ?int
17+
{
18+
$configKey = QueueRetryConfigInterface::CONFIG_KEY_NAME . '/' . $topicName;
19+
$queueRetryTopic = $this->configStorage->get($configKey);
20+
21+
if (!$queueRetryTopic) {
22+
return null;
23+
}
24+
25+
$retryLimitKey = QueueRetryConfigInterface::RETRY_LIMIT;
26+
return isset($queueRetryTopic[$retryLimitKey]) ? (int)$queueRetryTopic[$retryLimitKey] : null;
27+
}
28+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace RunAsRoot\MessageQueueRetry\SchemaLocator;
6+
7+
use Magento\Framework\Config\Dom\UrnResolver;
8+
use Magento\Framework\Config\SchemaLocatorInterface;
9+
use RunAsRoot\MessageQueueRetry\Config\QueueRetryConfigInterface;
10+
11+
class QueueRetrySchemaLocator implements SchemaLocatorInterface
12+
{
13+
public function __construct(private UrnResolver $urnResolver)
14+
{
15+
}
16+
17+
public function getSchema(): ?string
18+
{
19+
return $this->urnResolver->getRealPath(QueueRetryConfigInterface::XSD_FILE_URN);
20+
}
21+
22+
public function getPerFileSchema(): ?string
23+
{
24+
return $this->urnResolver->getRealPath(QueueRetryConfigInterface::XSD_FILE_URN);
25+
}
26+
}

src/Service/IsMessageShouldBeSavedForRetryService.php

Lines changed: 5 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,19 @@
44

55
namespace RunAsRoot\MessageQueueRetry\Service;
66

7-
use JsonException;
87
use Magento\Framework\MessageQueue\EnvelopeInterface;
8+
use RunAsRoot\MessageQueueRetry\Repository\Query\FindQueueRetryLimitByTopicNameQuery;
99
use RunAsRoot\MessageQueueRetry\System\Config\MessageQueueRetryConfig;
1010

1111
class IsMessageShouldBeSavedForRetryService
1212
{
1313
public function __construct(
1414
private MessageQueueRetryConfig $messageQueueRetryConfig,
15-
private GetMessageRetriesCountService $getMessageRetriesCountService
15+
private GetMessageRetriesCountService $getMessageRetriesCountService,
16+
private FindQueueRetryLimitByTopicNameQuery $findQueueRetryLimitByTopicNameQuery
1617
) {
1718
}
1819

19-
/**
20-
* @throws JsonException
21-
*/
2220
public function execute(EnvelopeInterface $message): bool
2321
{
2422
if (!$this->messageQueueRetryConfig->isDelayQueueEnabled()) {
@@ -38,24 +36,12 @@ public function execute(EnvelopeInterface $message): bool
3836
return false;
3937
}
4038

41-
$queueConfiguration = $this->getQueueConfiguration($topicName);
39+
$retryLimit = $this->findQueueRetryLimitByTopicNameQuery->execute($topicName);
4240

43-
if (!$queueConfiguration) {
41+
if ($retryLimit === null) {
4442
return false;
4543
}
4644

47-
$retryLimit = $queueConfiguration[MessageQueueRetryConfig::RETRY_LIMIT] ?? 0;
48-
4945
return $totalRetries >= $retryLimit;
5046
}
51-
52-
/**
53-
* @throws JsonException
54-
* @return array<string,mixed>|null
55-
*/
56-
private function getQueueConfiguration(string $topicName): ?array
57-
{
58-
$delayQueueConfiguration = $this->messageQueueRetryConfig->getDelayQueues();
59-
return $delayQueueConfiguration[$topicName] ?? null;
60-
}
6147
}

src/System/Config/MessageQueueRetryConfig.php

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,10 @@
44

55
namespace RunAsRoot\MessageQueueRetry\System\Config;
66

7-
use JsonException;
87
use Magento\Framework\App\Config\ScopeConfigInterface;
98

109
class MessageQueueRetryConfig
1110
{
12-
public const MAIN_TOPIC_NAME = 'main_topic_name';
13-
public const DELAY_TOPIC_NAME = 'delay_topic_name';
14-
public const RETRY_LIMIT = 'retry_limit';
15-
private const XML_PATH_DELAY_QUEUES = 'message_queue_retry/general/delay_queues';
1611
private const XML_PATH_ENABLE_DELAY_QUEUE = 'message_queue_retry/general/enable_delay_queue';
1712

1813
public function __construct(private ScopeConfigInterface $scopeConfig)
@@ -23,33 +18,4 @@ public function isDelayQueueEnabled(): bool
2318
{
2419
return $this->scopeConfig->isSetFlag(self::XML_PATH_ENABLE_DELAY_QUEUE);
2520
}
26-
27-
/**
28-
* @return array<int|string, array<string,mixed>>
29-
* @throws JsonException
30-
*/
31-
public function getDelayQueues(): array
32-
{
33-
$configValues = $this->scopeConfig->getValue(self::XML_PATH_DELAY_QUEUES);
34-
35-
if (!$configValues) {
36-
return [];
37-
}
38-
39-
$configValues = json_decode($configValues, true, 512, JSON_THROW_ON_ERROR);
40-
41-
$result = [];
42-
43-
foreach ($configValues as $configValue) {
44-
$mainTopicName = $configValue[self::MAIN_TOPIC_NAME] ?? null;
45-
$retryLimit = isset($configValue[self::RETRY_LIMIT]) ? (int)$configValue[self::RETRY_LIMIT] : null;
46-
$result[$mainTopicName] = [
47-
self::MAIN_TOPIC_NAME => $mainTopicName,
48-
self::DELAY_TOPIC_NAME => $configValue[self::DELAY_TOPIC_NAME] ?? null,
49-
self::RETRY_LIMIT => $retryLimit,
50-
];
51-
}
52-
53-
return $result;
54-
}
5521
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace RunAsRoot\MessageQueueRetry\Test\Unit\Converter;
4+
5+
use PHPUnit\Framework\TestCase;
6+
use RunAsRoot\MessageQueueRetry\Converter\QueueRetryXmlToArrayConverter;
7+
8+
final class QueueRetryXmlToArrayConverterTest extends TestCase
9+
{
10+
private QueueRetryXmlToArrayConverter $sut;
11+
12+
protected function setUp(): void
13+
{
14+
$this->sut = new QueueRetryXmlToArrayConverter();
15+
}
16+
17+
public function testConvert(): void
18+
{
19+
$doc = new \DOMDocument();
20+
$doc->loadXML($this->getQueueRetryXmlFile());
21+
22+
$result = $this->sut->convert($doc);
23+
24+
$expected = [
25+
'queue_retry_topics' => [
26+
'sample_topic' => [
27+
'topic_name' => 'sample_topic',
28+
'retry_limit' => 3,
29+
],
30+
'another_topic' => [
31+
'topic_name' => 'another_topic',
32+
'retry_limit' => 10,
33+
],
34+
],
35+
];
36+
37+
$this->assertEquals($expected, $result);
38+
}
39+
40+
public function getQueueRetryXmlFile(): string
41+
{
42+
return file_get_contents(__DIR__ . '/_files/queue_retry.xml');
43+
}
44+
}

0 commit comments

Comments
 (0)