skeeks / cms-job
Universal background jobs, queue and run history for SkeekS CMS
Package info
Type:yii2-extension
pkg:composer/skeeks/cms-job
Requires
- php: >=8.0
- skeeks/cms: ^6.4.9.25 || dev-master
- yiisoft/yii2-queue: ^2.3.8
Requires (Dev)
None
Suggests
- skeeks/cms-agent: ^3.2.5 for automatic maintenance schedules via cmsAgent/init
Provides
None
Conflicts
- skeeks/cms-agent: <3.2.5
Replaces
None
README
skeeks/cms-job отвечает за постановку и выполнение фоновых заданий, историю,
прогресс, отмену, повторные попытки и блокировки ресурсов. Расписания принадлежат
skeeks/cms-agent, а запуск и перезапуск служб — хостингу или администратору.
- Канал — именованная очередь, например
catalogилиmaintenance. - Тип задания — зарегистрированная операция с обработчиком, каналом и правилами выполнения.
- Запуск — конкретная операция с payload и записью
CmsJobRunв истории. - Диспетчер — один ожидающий PHP-процесс, запускающий отдельного ребёнка для каждого задания.
Доменному коду не нужно резервировать сообщения, создавать собственный worker
или вызывать классы yii2-queue: используйте Yii::$app->jobs.
Автоматическое обслуживание
Пакет регистрирует три типа в очереди maintenance: cms-job.cleanup
(просроченная история, раз в сутки) и cms-job.cleanup-logs (просроченные
приватные логи и CSV отчёты об ошибках, раз в час), cms-job.cleanup-workspaces
(временные рабочие папки завершённых заданий, раз в час). При установленном
skeeks/cms-agent >= 3.2.5 их расписания входят в общий конфиг пакета.
После обновления выполните php yii cmsAgent/init: повторный запуск не
создаёт дубли и сохраняет состояние ранее отключённых расписаний.
Нужны работающие cmsAgent/execute и потребитель очереди maintenance.
Очистки используют общий ресурсный lock и отдельные ключи дедупликации на
всю установку, обрабатывают данные порциями до 500 объектов каждого вида
и продолжают тот же запуск, пока просроченные данные не закончатся.
Незавершённые задания защищены; действуют существующие сроки хранения.
Для новых job со штатными приватными логами дополнительная очистка не нужна.
Временные исходники и промежуточные результаты новых job размещайте через
$context->getWorkspace()->path('offers.jsonl'). Пакет сохраняет папку между
продолжениями, защищает её блокировкой и очищает через 7 суток после успеха
или через 14 суток после предупреждений/ошибки/отмены/тайм-аута.
API, ограничения, dry-run и переход со старых папок описаны в WORKSPACES.md.
Незарегистрированные старые папки, включая supplier-imports, и файлы CMS storage
автоматически не удаляются.
Подробности внедрения и ручные команды — в DEPLOYMENT.md.
1. Зарегистрировать канал и тип задания
В общем конфиге проекта или пакета-потребителя, который загружают и web, и console, добавьте:
return [ 'components' => [ 'jobQueueFactory' => [ 'queues' => ['examples' => []], ], 'jobRegistry' => [ 'types' => [ 'example.calculate-total' => [ 'type' => 'example.calculate-total', 'title' => 'Подсчёт суммы', 'handler' => \app\jobs\CalculateTotalJobHandler::class, 'queue' => 'examples', 'timeout' => 60, 'leaseSeconds' => 30, 'idempotent' => true, 'maxAttempts' => 3, ], ], ], ], ];
Для Composer-пакета подключите этот файл через extra.config-plugin.web и
extra.config-plugin.console, как это сделано в composer.json.
Не регистрируйте канал только в web-конфиге: консольный диспетчер его не увидит.
Ядро объявляет default и maintenance; остальные каналы объявляет потребитель.
Регистрация канала сама по себе не запускает процесс и не создаёт подключение БД.
Используйте стабильные имена типов и каналов. Для управления службами хостинга
имя канала должно начинаться с буквы/цифры, содержать только буквы, цифры,
-, _ и иметь длину до 64 символов. Обработчики должны быть доступны через
Composer autoload. Несовместимый payload оформляйте новым типом, например .v2.
2. Написать обработчик
Ниже полностью исполняемый учебный пример без внешних побочных действий:
namespace app\jobs; use skeeks\cms\job\contracts\JobReporterInterface; use skeeks\cms\job\handlers\AbstractJobHandler; use skeeks\cms\job\runtime\JobContext; final class CalculateTotalJobHandler extends AbstractJobHandler { public function run(JobContext $context, JobReporterInterface $reporter): void { $values = $context->get('values', []); if (!is_array($values)) { throw new \InvalidArgumentException('Ожидался список чисел.'); } $reporter->setStage('calculate', 'Подсчёт суммы'); $reporter->setTotal(count($values)); $total = 0; foreach ($values as $value) { $reporter->heartbeat(); if ($reporter->isCancelled()) { return; } if (!is_numeric($value)) { throw new \InvalidArgumentException('Список содержит нечисловое значение.'); } $total += $value; $reporter->countSuccess(); $reporter->advance(); } $reporter->setResult(['total' => $total]); } }
В предметном обработчике вызывайте сервис своего пакета. Продлевайте аренду и проверяйте отмену во время долгой работы, а не только перед началом. Перед записью прогресса фиксируйте большие транзакции порциями: heartbeat внутри незавершённой транзакции не виден другим процессам. Не скрывайте ошибки за успешным возвратом из обработчика.
idempotent=true допустим только если повтор после неопределённого результата
безопасен. Для необратимых внешних действий оставьте false и одну попытку,
пока не реализована надёжная идемпотентность. Лимит на канал не заменяет
resourceKey для операций над общими данными и dedupKey/overlapPolicy
для повторной постановки. Эти правила задаются в
JobTypeDefinition, а не в транспортной очереди.
3. Поставить задание
$run = Yii::$app->jobs->push('example.calculate-total', [ 'values' => [10, 20, 30], ]); if ($run !== null) { $runId = $run->id; }
В payload передавайте JSON-совместимые значения и идентификаторы, а не модели,
соединения или PHP-замыкания. Канал выбирается определением типа. Постановка
через штатное общее DB-подключение участвует в транзакции приложения.
push() может вернуть null, когда политика пересечений пропустила дубль.
Не вставляйте записи прямо в cms_queue или cms_job_run.
Для повторения по расписанию используйте cms-agent с зарегистрированным
типом задания; расписание публикует запуск и не выполняет долгий обработчик
на web-запросе. Для запуска из UI задайте существующее RBAC-право типа задания
и проверьте доступ пользователя; произвольный маршрут не является правом.
4. Запустить диспетчер
Из корня установленного сайта:
php yii cms-job/worker/queues --json=1 php yii cms-job/worker/dispatch
Первая команда показывает итоговую конфигурацию без потребления сообщений.
Вторая сама читает jobQueueFactory.queues и опрашивает все каналы, включая
пустые. Пустой канал добавляет проверку БД, но не отдельный ожидающий PHP-процесс.
В конфигурации по умолчанию php yii cms-job/worker без канала также запускает
диспетчер. Привязки к cms-hosting, домену, VPS или конкретному серверу нет.
Требуются Linux/PHP с pcntl, транспорт DbQueue и изоляция заданий.
Настройки проекта:
'components' => [ 'jobWorker' => [ 'mode' => 'dispatcher', 'maxProcesses' => 10, 'channelConcurrency' => 1, 'channels' => ['examples' => 2], // необязательное исключение ], ],
Разные каналы могут выполняться параллельно. Пределы проверяются до резервирования сообщения. Ребёнок завершается после одного задания; обработка TTR, падения и токенов попыток общая с прежним воркером. В простое адаптер освобождает MySQL-соединение. Локальный lock исключает второй диспетчер этого сайта, но не ограничивает процессы на других серверах.
Когда подключится новый канал
Диспетчер читает конфигурацию при запуске, без перечитывания PHP-конфига на лету. После регистрации нового канала или изменения лимитов:
- Убедитесь, что
worker/queues --json=1показывает актуальный канал и его типы. - Корректно остановите диспетчер через SIGTERM и запустите снова. Он прекратит
резервирование и дождётся текущих детей. Для systemd используйте службу,
настроенную с достаточным
TimeoutStopSecиKillMode=mixed. - Если службой управляет
cms-hostingс политикой «Все каналы через диспетчер», перезапуск организует автоматическая сверка. Она обнаруживает новые каналы, отключает прежнюю конфигурацию и после завершения заданий включает новую. Штатный интервал сверки — 5 минут; переход может занять несколько сверок и время завершения текущих заданий.
Параметр --queues=examples,maintenance ограничивает диспетчер указанными
каналами. Новая очередь вне этого списка не появится после простого рестарта:
нужно также обновить список запуска. Хостинг формирует и обновляет его по
конфигурации сайта, исключая свой служебный hosting-control.
Не удаляйте канал или тип до обработки/явной отмены старых сообщений и завершения активных запусков. Изменение конфига не переносит накопленные задания.
Совместимость и эксплуатация
Прежние команды сохраняются:
php yii cms-job/worker --queue=maintenance php yii cms-job/worker/cron --queue=maintenance
Явный --queue всегда запускает отдельный воркер. jobWorker.mode=workers
отключает выбор диспетчера для команды без канала. Не запускайте старые воркеры
и диспетчер одновременно на одинаковых каналах, если нужны строгие лимиты.
Плановый --maxSeconds останавливает приём новых заданий и требует внешнего
менеджера процессов для повторного запуска; сам PHP-процесс себя не перезапускает.
Дальнейшая документация: