Продвинутое

Джобы

Джоба (Job) — самый частый вид фоновой задачи: разовая операция, запускаемая «выстрелил и забыл» из сервиса. Контроллер отвечает клиенту сразу, а тяжёлая работа уходит в отдельный процесс.

База Stereotype\JobЗапуск Job::dispatch($data)Метод resolution($data)

Что такое джоба и зачем

Джоба — единица фоновой работы, выполняемая вне запроса.

Проблема. Некоторые операции нельзя делать синхронно: отправка письма, генерация отчёта, создание папки инстанса. Держать под них запрос открытым — плохой UX и риск таймаута.

Решение. Оформите работу как Job и запустите её из сервиса через dispatch() — она форкнется в фон, а запрос вернёт ответ немедленно. Об этом и раздел; общая модель процессов — на странице Фоновые задачи.

Определение

Скаффолд флагом -J:

bash
php call make -J .InstanceCreate   # → main/InstanceCreateJob.php

Джоба реализует resolution() — тело задачи; зависимости приходят через #[Autowired], логгер доступен как $this->logger:

main/InstanceCreateJob.php
<?php

namespace Main;

use Flytachi\Winter\DI\Attribute\Autowired;
use Flytachi\Winter\K2\Stereotype\Job;

class InstanceCreateJob extends Job
{
  #[Autowired] private InstanceRepository $repo;

  public function resolution(mixed $data = null): void
  {
      if (! is_string($data)) {
          $this->logger->error('bad payload', ['data' => $data]);
          return;
      }

      $instance = $this->repo->findById($data);
      // ... создать папку, переключить статус CREATED → ACTIVE
  }
}

Запуск из сервиса

Типичный приём — dispatch() сразу после записи в БД. Запрос не ждёт джобу:

main/InstanceService.php
public function create(CreateInstanceDto $dto): Instance
{
  $instance = Instance::fromDto($dto);
  $instance->id = $this->repo->insert($instance);

  InstanceCreateJob::dispatch($instance->id);   // фон, запрос уже свободен

  return $instance;
}

Передача данных

Аргумент dispatch($data) уходит в потомок сериализованным. Передавайте идентификаторы и простые данные, а тяжёлые объекты подгружайте в resolution() из репозитория:

php
InstanceCreateJob::dispatch($instance->id);          // хорошо — id
InstanceCreateJob::dispatch(['id' => $id, 'retry' => 0]);  // хорошо — простой массив

DI в потомке, но не ресурсы

Форкнутый потомок пересоздаёт зависимости через контейнер (#[Autowired] работает), но живые соединения родителя не наследует — джоба сама достаёт данные из репозитория. Логи потомка идут в канал cli.

Идемпотентность

Потомок форкается «выстрелил и забыл»: родитель не видит его исключений (они логируются как critical, потомок завершается сам). Поэтому пишите джобы идемпотентно — безопасно к повтору и частичному выполнению:

php
public function resolution(mixed $data = null): void
{
  $instance = $this->repo->findById($data);
  if ($instance === null || $instance->status !== Status::CREATED->value) {
      return;   // уже обработано или удалено — выходим тихо
  }
  // ... выполнить шаг и перевести статус
}

Дальше