Middle: питання на співбесіді з теми «Планувальник»
Питання з реальних співбесід з відповідями: Laravel і PHP, бази даних, JavaScript і фронтенд, Git, Docker, API, безпека й архітектура. Тими самими темами, що й тести.
3 питання
Планувальник дозволяє описати періодичні завдання прямо в коді. У Laravel 11+ розклад визначається в routes/console.php через фасад Schedule:
use Illuminate\Support\Facades\Schedule;
Schedule::command('reports:send')->dailyAt('08:00');
Schedule::job(new PruneLogs)->weekly();
Schedule::call(fn () => Cache::flush())->hourly();
На сервері потрібен лише один cron-запис, що щохвилини викликає планувальник:
* * * * * cd /app && php artisan schedule:run >> /dev/null 2>&1
Корисні модифікатори: withoutOverlapping(), onOneServer(), runInBackground().
Розклад описують у routes/console.php ланцюжком методів:
Schedule::command('reports:send')
->weekdays()
->at('9:00')
->timezone('Europe/Kyiv');
Schedule::command('cache:prune-stale-tags')->hourly();
Schedule::job(new SyncRates)->everyFifteenMinutes()->between('8:00', '20:00');
Schedule::command('backup:run')->dailyAt('3:30')->environments(['production']);
Schedule::command('invoices:remind')->daily()->when(fn () => Setting::get('reminders_on'));
Групи методів:
- частота:
everyMinute(),everyFiveMinutes(),hourly(),daily(),weekly(),monthlyOn(1, '8:00'),cron('0 */6 * * *'); навітьeverySecond(); - дні:
weekdays(),weekends(),mondays(),days([1, 3]); - час:
between(),unlessBetween(); - умови:
when(),skip(),environments(); - пояс:
timezone()для завдання чиschedule_timezoneуconfig/app.phpдля всіх.
Пастка з поясом: якщо час сервера UTC, а бізнес у Києві, dailyAt('9:00') без поясу спрацює о 11 чи 12 за київським. З переходом на літній час завдання в проміжку 3:00-4:00 можуть пропуститися чи виконатися двічі - критичні запускають поза ним.
php artisan schedule:list показує, коли кожне завдання виконається наступного разу.
Дві проблеми: наступний запуск того самого завдання стартує, поки попереднє ще працює, і довге завдання затримує інші, заплановані на ту саму хвилину.
Накладання - withoutOverlapping():
Schedule::command('import:products')
->everyFiveMinutes()
->withoutOverlapping(30); // блокування на 30 хв на випадок аварії
Поки попередній запуск триває, новий пропускається. Блокування живе в кеші: якщо процес упав, воно спаде за вказаний час (за замовчуванням - 24 години, тож розумний ліміт варто задати).
Затримка інших - runInBackground():
Schedule::command('analytics:report')->daily()->runInBackground();
Завдання на той самий час виконуються послідовно; фонове не змушує наступні чекати. Працює для command() і exec().
Кращий шлях для важкого - щоб планувальник лише ставив роботу в чергу:
Schedule::job(new ImportProducts)->everyFiveMinutes();
Тоді планувальник звільняється миттєво, а повтори, таймаути й паралельність бере на себе черга (ShouldBeUnique проти дублів).
На кількох серверах до цього додається onOneServer() - зі спільним кешем.