Увійти Реєстрація
Блог Серії
Кар'єра
Вакансії Компанії
Навчання
Документація Співбесіди Тестування Відео
Екосистема
Пакети Ресурси Проєкти Інструменти Події
Інше
Про нас Реклама

Senior: питання на співбесіді з теми «Artisan»

Питання з реальних співбесід з відповідями: Laravel і PHP, бази даних, JavaScript і фронтенд, Git, Docker, API, безпека й архітектура. Тими самими темами, що й тести.

2 питання

Довгу команду переривають деплой, зупинка контейнера, 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) - щоб пам'ять не росла, а зміни не зсували сторінки.

Альтернатива для дуже великих обсягів - команда лише розбиває роботу на завдання черги (батчі), а повтори й паралельність бере на себе черга.

Докладніше в документації: Обробка сигналів

Команду запускають cron, CI, скрипти деплою - не люди. Їй треба повідомляти результат так, щоб це зрозуміла машина.

Коди виходу: 0 - успіх, будь-що інше - збій. Скрипт деплою й CI зупиняються саме на ненульовому коді.

if ($failed > 0) {
    $this->error("{$failed} записів не оброблено");
    return self::FAILURE;
}
return self::SUCCESS;

// або з будь-якого місця
$this->fail('Немає підключення до API');

Команда, що ловить винятки й завжди повертає 0, приховує збої від усього навколо.

Інше, що варто мати:

  • неінтерактивність: без confirm() і ask() у шляху виконання за розкладом; підтвердження руйнівних дій - через --force і перевірку isProduction();
  • журнал, а не лише консоль: $this->info() бачить лише той, хто дивиться в термінал; підсумок і помилки - ще й у Log;
  • --dry-run для команд, що змінюють дані, - показати, що буде змінено;
  • прогрес і підсумок (оброблено, пропущено, помилки) - щоб було видно, що команда не зависла;
  • тести: $this->artisan('reports:send', ['user' => 7])->assertSuccessful()->expectsOutputToContain('Надіслано').

Докладніше в документації: Коди виходу