Magento 2 intensywnie korzysta z crona – indeksowanie, wysyłka emaili, czyszczenie sesji, synchronizacja kursów walut. Gdy cron przestaje działać, sklep zaczyna się sypać w nieoczywisty sposób: ceny się nie aktualizują, emaile nie wychodzą, indeksy się starzeją. Pokazuję jak działa system crona w Magento, jak pisać własne joby i jak diagnozować problemy.
Jak Magento 2 obsługuje cron
Magento ma dwa procesy cron uruchamiane z poziomu systemu operacyjnego:
# Dwa wpisy w crontab serwera - uruchamiane co minutę * * * * * php /var/www/magento/bin/magento cron:run 2>&1 * * * * * php /var/www/magento/bin/magento cron:run --group=index 2>&1
cron:run to scheduler – sprawdza tabelę cron_schedule w bazie, planuje nowe joby i uruchamia te które są gotowe. Sam job nie wykonuje się bezpośrednio w procesie schedulera – Magento uruchamia go jako osobny proces PHP, dzięki czemu jeden zawieszony job nie blokuje pozostałych.
Własny job cron – konfiguracja
Nowy job wymaga dwóch plików: klasy PHP z logiką i pliku konfiguracyjnego crontab.xml.
Klasa jobu:
<?php
declare(strict_types=1);
namespace Vendor\Module\Cron;
use Psr\Log\LoggerInterface;
use Vendor\Module\Model\SyncService;
class SyncExternalPrices
{
public function __construct(
private SyncService $syncService,
private LoggerInterface $logger
) {}
public function execute(): void
{
$this->logger->info('SyncExternalPrices: start');
try {
$count = $this->syncService->syncPrices();
$this->logger->info('SyncExternalPrices: zakończono', ['updated' => $count]);
} catch (\Exception $e) {
// Loguj błąd, ale nie rzucaj wyjątku dalej
// Niezłapany wyjątek w cronie = job ze statusem "error" w cron_schedule
$this->logger->error('SyncExternalPrices: błąd', ['error' => $e->getMessage()]);
}
}
}
Konfiguracja w etc/crontab.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="vendor_module_sync_prices"
instance="Vendor\Module\Cron\SyncExternalPrices"
method="execute">
<!-- Co godzinę, 15 minut po pełnej -->
<schedule>15 * * * *</schedule>
</job>
</group>
</config>
Harmonogram konfigurowalny z panelu admina
Zamiast hardkodować wyrażenie cron w XML, możesz pozwolić adminowi na zmianę harmonogramu przez konfigurację:
<!-- etc/crontab.xml -->
<job name="vendor_module_sync_prices"
instance="Vendor\Module\Cron\SyncExternalPrices"
method="execute">
<config_path>vendor_module/cron/sync_prices_schedule</config_path>
</job>
<!-- etc/config.xml - wartość domyślna -->
<?xml version="1.0"?>
<config>
<default>
<vendor_module>
<cron>
<sync_prices_schedule>15 * * * *</sync_prices_schedule>
</cron>
</vendor_module>
</default>
</config>
<!-- etc/adminhtml/system.xml - pole w panelu -->
<field id="sync_prices_schedule" translate="label" type="text" sortOrder="10" showInDefault="1">
<label>Harmonogram synchronizacji cen</label>
<backend_model>Magento\Cron\Model\Config\Backend\Cron</backend_model>
<comment>Format cron: minuty godziny dni miesiące dni-tygodnia</comment>
</field>
Własna grupa cron – izolacja jobów
Domyślna grupa default miesza joby systemowe z Twoimi. Dla jobów długo działających lub wymagających specjalnej konfiguracji warto stworzyć własną grupę:
<!-- etc/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="vendor_module">
<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>60</history_success_lifetime>
<history_failure_lifetime>600</history_failure_lifetime>
<use_separate_process>1</use_separate_process>
</group>
</config>
Uruchomienie własnej grupy:
# Na serwerze - dodaj do crontab * * * * * php /var/www/magento/bin/magento cron:run --group=vendor_module 2>&1
Debugowanie crona – najczęstsze problemy
# Sprawdź stan tabeli cron_schedule bin/magento cron:run --group=default # Podgląd zaplanowanych jobów bezpośrednio w bazie # SELECT job_code, status, scheduled_at, executed_at, finished_at, messages # FROM cron_schedule # WHERE job_code = 'vendor_module_sync_prices' # ORDER BY scheduled_at DESC LIMIT 20; # Ręczne uruchomienie konkretnego jobu (Magento 2.4+) bin/magento cron:run --job-code=vendor_module_sync_prices # Wyczyść starą historię bin/magento cron:remove-entries --task=vendor_module_sync_prices
Statusy w tabeli cron_schedule:
| Status | Znaczenie |
|---|---|
pending |
Zaplanowany, czeka na wykonanie |
running |
Aktualnie wykonywany |
success |
Zakończony pomyślnie |
error |
Rzucił wyjątek – sprawdź kolumnę messages |
missed |
Nie zdążył się uruchomić w oknie czasowym |
Cron w DDEV
# Jednorazowe uruchomienie crona w DDEV ddev exec bin/magento cron:run # Lub wejdź do kontenera i ustaw crontab ddev ssh crontab -e # Dodaj: * * * * * php /var/www/html/bin/magento cron:run 2>&1
Podsumowanie
Cron w Magento 2 to więcej niż prosty scheduler – to system z grupami, historią, izolacją procesów i konfigurowalnym harmonogramem. Własna grupa cron dla krytycznych jobów, obsługa wyjątków w metodzie execute() i regularne sprawdzanie tabeli cron_schedule pod kątem statusu error – to minimum które warto mieć w każdym projekcie produkcyjnym.
