So here’s something you might already have stumbled into: Ever had the situation where you wanted a cronjob to run every minute, but it turned out it only ran every 15 minutes? Turns out this isn’t a bug, but rather a feature!
The Problem
So I recently was answering a question on Stack Exchange where somebody had exact this problem. And what do you know? I was having the exact same problem as well! After some research and diving into the Magento code (because that’s the best place to look if you want to figure out how Magento works) I concluded the following:
Cron jobs in Magento 2 are limited by their settings defined in the cron group they belong to.
Alex Gustav already points this out in his answer, but to clarify it a bit more: There are 2 cron groups by default in Magento 2: default
and index
. These are defined in Magento_Indexer/etc/cron_groups.xml
and Magento_Cron/etc/cron_groups.xml
. For example:
1 2 3 4 5 6 7 8 9 10 |
<group id="default"> <schedule_generate_every>15</schedule_generate_every> <schedule_ahead_for>20</schedule_ahead_for> <schedule_lifetime>15</schedule_lifetime> <history_cleanup_every>10</history_cleanup_every> <history_success_lifetime>60</history_success_lifetime> <history_failure_lifetime>600</history_failure_lifetime> <use_separate_process>0</use_separate_process> </group> |
These cron group determine the default / global values of every cron job. When I was doing my own tests I concluded that you cannot set a cron to run more often per hour than determined in the schedule_generate_every
-setting. So for example, if you have something like this:
1 2 3 4 5 6 |
<group id="default"> <job name="custom_module" instance="Custom\Module\Cron\DoSomething" method="execute"> <schedule>*/1 * * * *</schedule> </job> </group> |
Even though you might expect that this cronjob is scheduled every minute, it’s global settings limit it to a minimum timeframe of 15 minutes.
The Solution
To “solve” this feature, you can either:
- Edit the setting in the system configuration (Advanced > System > Cron), or:
- Override the global default in your module by adding a
cron_groups.xml
-file of your own (assuming that the client did not already tinker with the configuration settings):
Example config_groups.xml
-file:
1 2 3 4 5 6 7 |
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Cron:etc/cron_groups.xsd"> <group id="default"> <schedule_generate_every>1</schedule_generate_every> </group> </config> |
Bug or Feature?
If you run into this the first time, you could argue that this is a bug; after all: Magento is not behaving like you’re expecting it to behave!
However…
You could argue that it’s a good thing that you have a global scope that can limit modules to try to run a cron every minute. Imagine that 100+ modules are trying to perform a task every minute. This could have a huge impact on the performance of your Magento installation.
On the other hand: If you’re a developer who knows what you’re doing, there might be good reasons to run a cron every minute.
Let’s dive in the code for this one!
Like I said before, the best way to understand how Magento works, is by looking at the code. So upon further inspecting the code I found the following:
The code responsible for scheduling the queue is Magento\Cron\Observer\ProcessCronQueueObserver::_generate()
. If you take a closer look at this code, you’ll immediately see the following:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
/** * check if schedule generation is needed */ $lastRun = (int)$this->_cache->load(self::CACHE_KEY_LAST_SCHEDULE_GENERATE_AT . $groupId); $rawSchedulePeriod = (int)$this->_scopeConfig->getValue( 'system/cron/' . $groupId . '/' . self::XML_PATH_SCHEDULE_GENERATE_EVERY, \Magento\Store\Model\ScopeInterface::SCOPE_STORE ); $schedulePeriod = $rawSchedulePeriod * self::SECONDS_IN_MINUTE; if ($lastRun > $this->timezone->scopeTimeStamp() - $schedulePeriod) { return $this; } |
So there you have it: it’s a feature. Intended behaviour. The code literally checks what the last time was a schedule was generated, and checks if it’s time to create a new one. All code below these lines, the code that actually adds the records to the cron_schedule
, don’t get executed if the amount if minutes entered in XML_PATH_SCHEDULE_GENERATE_EVERY
has not yet passed.
In Conclusion
If running a cron every minute is mandatory for you, you should add a new cron group for it. Don’t pollute the index
cron group with non-indexing cron jobs (unless your job is an indexing job of course!), and don’t lower the schedule_generate_every
of the default
cron group.
But in the end it’s all a matter of architecture and I’ll leave that up to you.
Visitors give this article an average rating of 1.7 out of 5.
How would you rate this article?
★ ★ ★ ★ ★