Довгу команду переривають деплой, зупинка контейнера, OOM. Вона має завершуватися чисто й продовжувати, а не починати все з нуля чи дублювати роботу.
1. Реагувати на сигнал:
public function handle(): int
{
$this->trap([SIGTERM, SIGINT], fn () => $this->shouldStop = true);
Order::query()->where('synced', false)->lazyById(500)->each(function (Order $order) {
if ($this->shouldStop) {
return false; // вийти після поточного запису
}
$this->sync($order);
$order->update(['synced' => true]);
});
return self::SUCCESS;
}
2. Пам'ятати прогрес у даних, а не в пам'яті: позначка synced, останній оброблений id у таблиці чи кеші. Перезапуск продовжить з місця зупинки.
3. Ідемпотентність кроку: повторна обробка того самого запису не має шкодити - перевірка стану чи унікальний ключ.
4. Один екземпляр: Isolatable і --isolated або withoutOverlapping() у планувальнику.
5. Обходити дані порціями (lazyById, chunkById) - щоб пам'ять не росла, а зміни не зсували сторінки.
Альтернатива для дуже великих обсягів - команда лише розбиває роботу на завдання черги (батчі), а повтори й паралельність бере на себе черга.