Intro To The Cron System In Magento 2
The goal of this post is to describe the cron system in Magento 2, how the core uses the system, and how you can leverage the system in your custom modules to schedule out-of-band tasks.
I am going to start by describing the requirements for your base system(the server that magento is running on). For the purposes of this tutorial I am going to assume that you are running your Magento 2 instance on Ubuntu Linux and that you have installed Magento 2 via composer. I will also assume that you are using apache as the web server with PHP-FPM to handle PHP file requests and that both of these are running as the www-data user(the child processes that is) and that www-data also owns the magento files under the document root. We will start by adding the following lines to www-data’s crontab.
This should open a text editor with the current contents of the user’s crontab. If it does not and instead gives you a prompt you can either choose an editor from the menu or add these two lines to that user’s .bashrc file so that the vim editor will automatically be used to open the crontab.
After you have the crontab open add the following line:
The above line assumes that the path to the instance of PHP that PHP-FPM is running can be accessed at /usr/bin/php and that your magento files live under a document root of /srv/www/example.com/public_html/.
The first time the bin/magento cron:run cronjob is run it will schedule all of the magento cron tasks by placing an entry in the core cron_schedule table. This entry contains information such as the time the task is schedule to run at, when it was ran(if it has already started running), the time that it completed at(if it is a past task), and whether the task finished successfully or encountered an error. A more complete breakdown of the process is:
- Register any jobs that need to be registered in the core table.
- Run any pending job entries from the core table that have a “start at” time equal to or less than the current time(scheduled to run at this exact moment or in the past but haven’t been run yet).
- Set the executed and completed at times in the core table for those tasks that were just run.
In M2’s cron system cron jobs belong to a cron group. These groups are just logical organizations of related tasks. However, there are some very important configuration setting for these groups, all of which are explained here in the devdocs: http://devdocs.magento.com/guides/v2.0/config-guide/cli/config-cli-subcommands-cron.html
One of the more important configuration values for a cron group is whether the tasks belonging to that group should be run in series or in parallel. Running a group’s tasks in parallel should allow you some significant performance benefits assuming that you have not created implicit dependencies between those tasks. You will also likely notice a much more significant resource usage spike if you run all of your tasks in parallel, so take this into consideration when analyzing the resource requirements of your servers and if you even need the benefits of running these tasks in parallel.
Now we will create a custom cron group for a sample custom module and define and run a cron task for our module.
First we will need to define our custom cron group and its options in the cron_groups.xml file located at app/code/BourneWebSec/HelloWorld/etc/cron_groups.xml with the following contents:
The gist of the above configuration is that a schedule entry will be written, at most, once every two minutes to the cron_schedule table and the other settings pertain to keeping the table relatively clean. However, this frequency is based upon what you have your crontab entry for the bin/magento cron:run command scheduled as. If the crontab entry is scheduled to run every 10 minutes then that will supercede the 2 minute threshold that you have configured in your cron group. Since we have configured our crontab entry to run every minute this will mean that not every run of that cronjob will cause a record to be added to the cron_schedule table. The other two things I want to mention about the above configuration are that we set the very long lifetime for failing tasks so that we can address the root cause of their failure, and we configured the tasks for this group to run in series, not in parallel.
Second we need to create a crontab.xml file at app/code/BourneWebSec/HelloWorld/etc/crontab.xml with the following contents:
Note that I am not going to cover the basic requirements of creating a custom module, that is out of scope for this post.
Next we will define the model and its sayHello() method. The model class will be located at app/code/BourneWebSec/HelloWorld/Model/HelloWorld.php with the following contents:
So when our cron task is run the sayHello() method will be called and simply append the message “Hello from the BourneWebSec_HelloWorld module!” to our magento.cron.log file under var/log/.
Here we defined our own custom cron group however, if you are fine with using either of the core cron groups; default or index then you can just use one of those. For more information about the options used for the default cron group you can look at its declaration in vendor/magento/module-cron/etc/cron_group.xml or for info about the index group look at its declaration in vendor/magento/module-indexer/etc/cron_group.xml.
After making these changes clear your cache with the following two commands and after a minute or so you should be able to see evidence that our cron has run by looking either in the core cron_schedule table or at the magento.cron.log file.
You can use the following SQL query to see the outcome of our custom module’s cron task:
Resources for further reading:
- http://devdocs.magento.com/guides/v2.0/config-guide/cli/config-cli-subcommands-cron.html