Magento1の時代からMagentoを触っている人にとっては、MagentoがCronによる定時バッチで様々なデータを更新・処理していることはよく知られていることはないでしょうか。

もちろんMagento2にもCronの仕組みはありますが、Magento1よりもいろいろと改善されています。
今回はMagento2のCronについて取り上げたいと思います。

Cronジョブは正しくセットアップしないと動かない

公式のDevDocsにも書かれていますが、Magentoのインストール後にはCronジョブを正しくセットアップする必要があります。

Magento2は原則Linuxサーバー上で動かす前提で作られているので、Linuxサーバーの設定に慣れている方であれば、Cronジョブの定義はさほど難しくはないでしょう。
Devdocsに示されている定義例をもとに、お使いの環境に合わせて定義を行ってください。

Cronジョブの作り方

Magentoのカスタマイズを進める上で、Cronジョブの作り方を知っていると、外部システム連携が行いやすくなります。
ここでは既に何かしらの独自エクステンションがある前提で説明を進めます。Magentoのエクステンション作成については他の記事を参照してください。

XMLファイルの定義

最初にXMLファイルを定義しておきます。
Cronジョブを定義する場合は、エクステンションディレクトリ内に「etc/crontab.xml」を作成します。
(既にある場合は定義を追記します)

XMLの内容は概ね決まっていて、以下のように定義します。

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Cron:etc/crontab.xsd">
    <group id="default">
        <job name="example_job"
             instance="My\Example\Cron\Example"
             method="execute">
            <schedule>*/5 * * * *</schedule>
        </job>
    </group>
</config>

複数のジョブを定義したい場合は、jobタグを複数定義します。
ただし、jobタグはgroupタグの子要素と決まっているので、XSDが定義していない場所にタグを書いてはいけません。

Cronジョブを担当するクラスの作成

続いて、ジョブを担当するクラスを作成します。
先程crontab.xmlで定義したクラス名のモジュールを作成します。

namespace My\Example\Cron;

class Example
{

    public function execute()
    {
     ... 具体的な処理
    }

}

コンストラクタやその他のメソッドは必要に応じて調整してください。
ObserverやPluginの場合は呼び出し元やフック元から渡されるパラメータがありますが、Cronの場合は何も渡されないので、処理目的に合わせたクラスをコンストラクタで定義する必要があります。
(このあたりはMagento2のDependency Injectionの仕組みを理解するとわかります)

Cronグループとは

Magento2から導入された概念として「Cronグループ」というものがあります。
Magento1では、1つのCronジョブが長時間処理を続けてしまうと、後続するジョブがすべて遅延するという問題がありました。

Magento2では、Cronグループが導入されたことで、ジョブを任意のグループに分けることができるようになり、処理に時間を要するジョブを独立して実行することができるようになりました。

Cronグループの定義

Cronグループを定義するには、crontab.xmlと同じ場所に、「cron_groups.xml」を作成し、以下のような内容を書き込みます。

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Cron:etc/cron_groups.xsd">
    <group id="my_cron_group">
        <schedule_generate_every>1</schedule_generate_every>
        <schedule_ahead_for>4</schedule_ahead_for>
        <schedule_lifetime>2</schedule_lifetime>
        <history_cleanup_every>10</history_cleanup_every>
        <history_success_lifetime>10</history_success_lifetime>
        <history_failure_lifetime>30</history_failure_lifetime>
        <use_separate_process>1</use_separate_process>
    </group>
</config>

あとは先程定義したcrontab.xmlのgroupタグのid属性を変えておきます。
これで独自のCronグループが定義でき、ジョブを他の処理から分割できました。

Cronジョブの罠

現時点のMagento2系安定版では、同じCronジョブが複数同時に実行されるという現象が報告されています。
将来的には解決される見通しになっていますが、ミッションクリティカルな処理でCronジョブを使用する場合は、排他制御などに十分注意する必要があるでしょう。