Penjadwalan tugas
Penjadwalan Laravel menaruh semua tugas berkala di dalam kode yang ter-review. Yang berubah saat aplikasi naik skala: kamu harus memastikan hanya satu server yang benar-benar menjalankannya.
Intisari
- Jadwal ditulis di
routes/console.php, dijalankan oleh satu entri cron per menit. ->onOneServer()wajib begitu ada lebih dari satu instance โ kalau tidak, tugas berjalan berkali-kali.->withoutOverlapping()mencegah tugas lambat menumpuk di atas dirinya sendiri.- Tugas berat sebaiknya cuma mengantre job, bukan mengerjakannya di proses penjadwal.
- Di ECS, entri cron digantikan EventBridge Scheduler atau satu task khusus โ dibahas di Fase 9.
Mendefinisikan jadwal
// routes/console.php
use Illuminate\Support\Facades\Schedule;
Schedule::command('keranjang:bersihkan --hari=7')
->dailyAt('03:00')
->timezone('Asia/Jakarta')
->onOneServer()
->withoutOverlapping()
->runInBackground();
Schedule::job(new SinkronKatalog)->hourly();
Schedule::call(fn () => Cache::forget('statistik'))->everyThirtyMinutes();
Schedule::command('laporan:bulanan')
->monthlyOn(1, '06:00')
->emailOutputOnFailure('[email protected]');
php artisan schedule:list # lihat semuanya beserta waktu jalan berikutnya
php artisan schedule:run # jalankan yang jatuh tempo sekarang
php artisan schedule:test # jalankan satu tugas secara manual
Satu entri cron untuk semuanya
* * * * * cd /var/www/app && php artisan schedule:run >> /dev/null 2>&1
Cron menjalankan schedule:run setiap menit; Laravel yang memutuskan tugas mana yang jatuh tempo.
Keuntungannya besar: menambah jadwal baru adalah perubahan kode yang ter-review, bukan perubahan crontab di
server yang tidak ada riwayatnya.
Dua pengaman yang berbeda
| Method | Mencegah | Butuh |
|---|---|---|
onOneServer() | Tugas yang sama berjalan di beberapa server sekaligus | Cache bersama (Redis / DynamoDB) |
withoutOverlapping() | Tugas berjalan lagi padahal yang sebelumnya belum selesai | Cache bersama |
Ini kesalahan produksi yang khas. Aplikasi berjalan di tiga kontainer; ketiganya menjalankan
schedule:run; email ringkasan harian terkirim tiga kali. onOneServer() memakai kunci
di cache bersama supaya hanya satu yang menang. Syaratnya: driver cache-nya harus bersama โ
file atau array tidak akan menolong karena setiap kontainer punya salinannya sendiri.
Jangan bekerja di dalam penjadwal
// KURANG BAIK โ proses penjadwal menahan pekerjaan panjang
Schedule::call(function () {
foreach (Pengguna::all() as $u) {
(new BuatLaporan)->untuk($u); // 20 menit di satu proses
}
})->daily();
// LEBIH BAIK โ penjadwal cuma mengantre; worker yang bekerja, dan bisa diskalakan
Schedule::call(function () {
Pengguna::query()->select('id')->chunkById(500, function ($batch) {
BuatLaporan::dispatch($batch->pluck('id')->all());
});
})->dailyAt('02:00');
Alasannya bukan kerapian: proses penjadwal yang berjalan 20 menit akan bertabrakan dengan jadwal berikutnya, tidak punya mekanisme percobaan ulang, dan tidak terlihat di dasbor antrean mana pun.
Menghindari beban serentak
Schedule::command('laporan:harian')->dailyAt('03:00');
Schedule::command('arsip:bersihkan')->dailyAt('03:07'); // digeser, bukan ditumpuk
Schedule::command('indeks:bangun')->dailyAt('03:15');
Menjadwalkan sepuluh tugas pada pukul 03:00 berarti sepuluh proses PHP menyala serentak dan sepuluh gelombang query mengenai database bersamaan. Menggeser beberapa menit tidak mengubah apa pun bagi bisnis, tapi mengubah banyak hal bagi database.
Di lingkungan container
Kontainer aplikasi biasanya tidak menjalankan cron. Ada dua pola yang lazim di AWS, dan keduanya dibahas di Fase 9:
| Pola | Cara kerja | Catatan |
|---|---|---|
| Task penjadwal khusus | Satu ECS service berisi satu task yang menjalankan schedule:work | Sederhana; tetap pasang onOneServer() saat deploy berlangsung |
| EventBridge Scheduler | EventBridge menjalankan task ECS sekali per menit | Tidak ada proses menganggur, tapi ada biaya start task |
Perhatikan zona waktu. Kontainer hampir selalu berjalan dalam UTC. dailyAt('03:00')
berarti pukul 3 pagi UTC โ pukul 10 pagi WIB. Sebutkan ->timezone('Asia/Jakarta') secara
eksplisit untuk setiap jadwal yang dipahami manusia sebagai jam kerja.
Latihan: jadwalkan perintah toko:ringkasan dari Fase 0 supaya berjalan tiap lima menit
dengan withoutOverlapping(), jalankan php artisan schedule:list dan pastikan waktu
berikutnya sesuai zona waktumu. Lalu buat perintah itu sleep(400) dan buktikan bahwa jalannya
yang kedua benar-benar dilewati.
Rangkuman ini sengaja dipangkas ke bagian yang dipakai di roadmap. Buka sumber aslinya saat kamu butuh detail lengkap atau referensi parameter.