Skip to content

Commit 2677547

Browse files
authored
Merge pull request #1727 from algolia/feat/MAGE-347-configurable-cronjob
MAGE-347: Configurable cronjob for the indexing queue
2 parents c6c8130 + 99462cb commit 2677547

File tree

9 files changed

+226
-2
lines changed

9 files changed

+226
-2
lines changed

Cron/ProcessQueue.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
namespace Algolia\AlgoliaSearch\Cron;
4+
5+
use Algolia\AlgoliaSearch\Helper\ConfigHelper;
6+
use Algolia\AlgoliaSearch\Model\Queue;
7+
use Algolia\AlgoliaSearch\Service\AlgoliaCredentialsManager;
8+
9+
class ProcessQueue
10+
{
11+
public function __construct(
12+
protected ConfigHelper $configHelper,
13+
protected Queue $queue,
14+
protected AlgoliaCredentialsManager $algoliaCredentialsManager
15+
) {}
16+
17+
public function execute()
18+
{
19+
if (!$this->configHelper->isQueueIndexerEnabled() || !$this->configHelper->useBuiltInCron()) {
20+
return;
21+
}
22+
23+
if (!$this->algoliaCredentialsManager->checkCredentialsWithSearchOnlyAPIKey()) {
24+
$this->algoliaCredentialsManager->displayErrorMessage(self::class);
25+
26+
return;
27+
}
28+
29+
$this->queue->runCron();
30+
}
31+
}

Exception/InvalidCronException.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?php
2+
3+
namespace Algolia\AlgoliaSearch\Exception;
4+
5+
class InvalidCronException extends \Exception
6+
{
7+
}

Helper/ConfigHelper.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ class ConfigHelper
7373
public const CATEGORY_SEPARATOR = 'algoliasearch_categories/categories/category_separator';
7474

7575
public const IS_ACTIVE = 'algoliasearch_queue/queue/active';
76+
public const USE_BUILT_IN_CRON = 'algoliasearch_queue/queue/use_built_in_cron';
7677
public const NUMBER_OF_JOB_TO_RUN = 'algoliasearch_queue/queue/number_of_job_to_run';
7778
public const RETRY_LIMIT = 'algoliasearch_queue/queue/number_of_retries';
7879

@@ -615,6 +616,15 @@ public function isQueueActive($storeId = null)
615616
return $this->configInterface->isSetFlag(self::IS_ACTIVE, ScopeInterface::SCOPE_STORE, $storeId);
616617
}
617618

619+
/**
620+
* @param $storeId
621+
* @return bool
622+
*/
623+
public function useBuiltInCron($storeId = null)
624+
{
625+
return $this->configInterface->isSetFlag(self::USE_BUILT_IN_CRON, ScopeInterface::SCOPE_STORE, $storeId);
626+
}
627+
618628
/**
619629
* @param $storeId
620630
* @return bool

Model/Backend/QueueCron.php

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
namespace Algolia\AlgoliaSearch\Model\Backend;
4+
5+
use Algolia\AlgoliaSearch\Exception\InvalidCronException;
6+
use Magento\Framework\App\Config\Value;
7+
8+
class QueueCron extends Value
9+
{
10+
const CRON_REGEX = '/^(\*|[0-9,\-\/\*]+)\s+(\*|[0-9,\-\/\*]+)\s+(\*|[0-9,\-\/\*]+)\s+(\*|[0-9,\-\/\*]+)\s+(\*|[0-9,\-\/\*]+)$/';
11+
12+
protected array $mappings = [
13+
'@yearly' => '0 0 1 1 *',
14+
'@annually' => '0 0 1 1 *',
15+
'@monthly' => '0 0 1 * *',
16+
'@weekly' => '0 0 * * 0',
17+
'@daily' => '0 0 * * *',
18+
'@hourly' => '0 * * * *'
19+
];
20+
21+
public function beforeSave()
22+
{
23+
$value = trim($this->getData('value'));
24+
25+
if (isset($this->mappings[$value])) {
26+
$value = $this->mappings[$value];
27+
$this->setValue($value);
28+
}
29+
30+
if (!preg_match(self::CRON_REGEX, $value)) {
31+
throw new InvalidCronException("Cron expression \"$value\" is not valid.");
32+
}
33+
34+
return parent::beforeSave();
35+
}
36+
}

Model/Config/EnableQueueComment.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public function getCommentText($elementValue)
1515
{
1616
$url = $this->urlInterface->getUrl('algolia_algoliasearch/queue/index');
1717

18-
return 'If enabled, all indexing operations (add, remove & update operations) will be done asynchronously using the CRON mechanism.<br><br/> Go to <a href="' . $url . '">Indexing Queue</a>. <br/> <br/>To schedule the run you need to add this in your crontab:<br>
19-
<code>*/5 * * * * php /absolute/path/to/magento/bin/magento indexer:reindex algolia_queue_runner</code> <br/><br/> <span class="algolia-config-warning">&#9888;</span> Enabling this option is recommended in production or if your store has a lot of products. ';
18+
return 'If enabled, all indexing operations (add, remove & update operations) will be done asynchronously using the cron mechanism.<br><br/> Go to <a href="' . $url . '">Indexing Queue</a>. <br/>
19+
<span class="algolia-config-warning">&#9888;</span> Enabling this option is recommended in production or if your store has a lot of products. ';
2020
}
2121
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
<?php
2+
3+
namespace Algolia\AlgoliaSearch\Test\Unit\Model\Backend;
4+
5+
use Algolia\AlgoliaSearch\Exception\InvalidCronException;
6+
use Algolia\AlgoliaSearch\Model\Backend\QueueCron;
7+
use Magento\Framework\App\Cache\TypeListInterface;
8+
use Magento\Framework\App\Config\ScopeConfigInterface;
9+
use Magento\Framework\Event\ManagerInterface;
10+
use Magento\Framework\Model\Context;
11+
use Magento\Framework\Registry;
12+
use PHPUnit\Framework\TestCase;
13+
14+
class QueueCronTest extends TestCase
15+
{
16+
protected ?QueueCron $queueCronModel;
17+
18+
protected function setUp(): void
19+
{
20+
$context = $this->createMock(Context::class);
21+
$eventDispatcher = $this->createMock(ManagerInterface::class);
22+
$context->method('getEventDispatcher')->willReturn($eventDispatcher);
23+
24+
$registry = $this->createMock(Registry::class);
25+
$config = $this->createMock(ScopeConfigInterface::class);
26+
$cacheTypeList = $this->createMock(TypeListInterface::class);
27+
28+
$this->queueCronModel = new QueueCron($context, $registry, $config, $cacheTypeList);
29+
}
30+
31+
/**
32+
* @dataProvider valuesProvider
33+
*/
34+
public function testInput($value, $isValid): void
35+
{
36+
$this->queueCronModel->setValue($value);
37+
38+
try {
39+
$result = $this->queueCronModel->beforeSave();
40+
$this->assertIsObject($result);
41+
} catch (InvalidCronException $exception) {
42+
$this->assertEquals(
43+
false,
44+
$isValid,
45+
"Cron expression \"$value\" is not valid but it should be."
46+
);
47+
48+
$this->assertEquals(
49+
"Cron expression \"$value\" is not valid.",
50+
$exception->getMessage()
51+
);
52+
}
53+
}
54+
55+
public function valuesProvider(): array
56+
{
57+
return [
58+
[
59+
'value' => '',
60+
'isValid' => false
61+
],
62+
[
63+
'value' => 'foo',
64+
'isValid' => false
65+
],
66+
[
67+
'value' => '*/5 * * * *',
68+
'isValid' => true
69+
],
70+
[
71+
'value' => '*/10 * * * *',
72+
'isValid' => true
73+
],
74+
[
75+
'value' => '0 0 1 1 *',
76+
'isValid' => true
77+
],
78+
[
79+
'value' => '0 0 * * 5',
80+
'isValid' => true
81+
],
82+
[
83+
'value' => '*/10 * * *', // One less property
84+
'isValid' => false
85+
],
86+
[
87+
'value' => '*/10 * * * * *', // One more property
88+
'isValid' => false
89+
],
90+
[
91+
'value' => '@daily', // Working alias
92+
'isValid' => true
93+
],
94+
[
95+
'value' => '@foo', // Not working alias
96+
'isValid' => false
97+
]
98+
];
99+
}
100+
101+
}

etc/adminhtml/system.xml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -984,6 +984,31 @@
984984
<model>Algolia\AlgoliaSearch\Model\Config\EnableQueueComment</model>
985985
</comment>
986986
</field>
987+
<field id="use_built_in_cron" translate="label comment" type="select" sortOrder="15" showInDefault="1" showInWebsite="0" showInStore="0">
988+
<label>Use Magento built-in cron</label>
989+
<source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
990+
<comment>
991+
<![CDATA[
992+
This configuration will add <code>algolia_queue_process</code> jobs to the Magento <code>cron_schedule</code> table according to the specified cron expression.
993+
]]>
994+
</comment>
995+
<depends>
996+
<field id="active">1</field>
997+
</depends>
998+
</field>
999+
<field id="cron_expr" translate="label comment" type="text" sortOrder="20" showInDefault="1" showInWebsite="0" showInStore="0">
1000+
<backend_model>Algolia\AlgoliaSearch\Model\Backend\QueueCron</backend_model>
1001+
<label>Cron expression</label>
1002+
<comment>
1003+
<![CDATA[
1004+
Enter a valid cron expression such as <code>*/5 * * * *</code> to process the queue every 5 minutes. You can also use aliases like <code>@hourly</code>.
1005+
]]>
1006+
</comment>
1007+
<depends>
1008+
<field id="active">1</field>
1009+
<field id="use_built_in_cron">1</field>
1010+
</depends>
1011+
</field>
9871012
<field id="number_of_job_to_run" translate="label comment" type="text" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="1">
9881013
<validate>validate-digits</validate>
9891014
<label>Number of jobs to run each time the cron is run</label>

etc/config.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,12 @@
5757
<category_separator> /// </category_separator>
5858
</categories>
5959
</algoliasearch_categories>
60+
<algoliasearch_queue>
61+
<queue>
62+
<use_built_in_cron>0</use_built_in_cron>
63+
<cron_expr>*/5 * * * *</cron_expr>
64+
</queue>
65+
</algoliasearch_queue>
6066
<algoliasearch_indexing_manager>
6167
<full_indexing>
6268
<products>0</products>

etc/crontab.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?xml version="1.0"?>
2+
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Cron:etc/crontab.xsd">
3+
<group id="algolia">
4+
<job name="algolia_queue_process" instance="Algolia\AlgoliaSearch\Cron\ProcessQueue" method="execute">
5+
<config_path>algoliasearch_queue/queue/cron_expr</config_path>
6+
</job>
7+
</group>
8+
</config>

0 commit comments

Comments
 (0)