Queue: Kerja Berat di Background
Pindahkan pekerjaan lambat (email, resize gambar) ke antrian.
Analogi Warung: Kenapa Queue Ada
Bayangkan warung kopi: satu kasir melayani pembeli. Kalau setiap pembeli pesan kopi tubruk yang butuh 5 menit diseduh dan kasir menunggu seduhannya selesai sebelum melayani pembeli berikutnya, antrean macet total. Solusi warung yang benar: kasir catat pesanan, tempel di dinding dapur (antrean), lanjut layani pembeli berikutnya. Tukang seduh mengerjakan pesanan satu per satu di belakang.
Itulah queue. Request HTTP adalah kasir yang harus merespons dalam milidetik. Pekerjaan lambat (kirim 1000 email, resize 200 foto, generate laporan) ditempel ke antrean, worker (tukang seduh) mengerjakannya di background. User tidak pernah menunggu.
Setup: Mulai dari Database Driver
Untuk belajar, driver database cukup, tidak butuh Redis:
# .env
QUEUE_CONNECTION=databasephp artisan queue:table
php artisan migrate
php artisan queue:workTiga perintah itu membuat tabel jobs dan failed_jobs, lalu menjalankan worker yang terus memantau tabel jobs dan mengeksekusi job yang masuk. Biarkan terminal worker tetap jalan di tab terpisah saat development.
Mengirim pekerjaan ke antrean semudah mengganti satu kata:
use App\Mail\InvoiceMail;
use Illuminate\Support\Facades\Mail;
// sinkron (user menunggu SMTP): ->send(...)
// antre (langsung kembali): ->queue(...)
Mail::to($user->email)->queue(new InvoiceMail('INV-001', 150000, 'invoices/INV-001.pdf'));
// notification bisa di-delay:
$user->notify((new KomentarDibalas($komentar))->delay(now()->addMinutes(30)));Konfigurasi Penting di config/queue.php
// config/queue.php (koneksi database)
'connections' => [
'database' => [
'driver' => 'database',
'table' => 'jobs',
'queue' => 'default',
'retry_after' => 90, // job dianggap gagal kalau > 90 detik tanpa kabar
'after_commit' => true, // PENTING: dispatch hanya setelah DB transaction commit
],
],after_commit => true adalah penyelamat yang sering dilupakan. Tanpa ini, kalau kamu dispatch job di dalam DB::transaction() lalu transaction di-rollback, job terlanjur masuk antrean dan worker mengeksekusi pekerjaan untuk data yang tidak jadi tersimpan. Dengan after_commit, job menunggu sampai transaction benar-benar sukses.
Production: Worker Harus Hidup Terus
php artisan queue:work yang dijalankan manual di terminal akan mati saat kamu logout SSH atau server restart. Di production, worker dijalankan via Supervisor (atau systemd) agar otomatis restart:
; /etc/supervisor/conf.d/laravel-worker.conf
[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/tokomu/artisan queue:work --sleep=3 --tries=3 --max-time=3600
autostart=true
autorestart=true
numprocs=2
user=www-dataLalu sudo supervisorctl reread && sudo supervisorctl update. Dua hal yang wajib diingat: setiap deploy kode baru, jalankan php artisan queue:restart agar worker memuat kode terbaru (worker long-lived menyimpan kode lama di memori!). Dan pisahkan antrean berat ke queue name sendiri (--queue=berat,default) supaya email penting tidak antre di belakang resize 500 foto.
Monitor dan Retry
php artisan queue:failed # daftar job yang gagal total
php artisan queue:retry all # coba lagi semuanya
php artisan queue:flush # hapus semua yang gagal (hati-hati)
php artisan queue:monitor default:100 # alert kalau antrean > 100Jebakan Umum
Driver sync di production. Default .env Laravel adalah QUEUE_CONNECTION=sync, artinya semua "queue" jalan sinkron di request. Cocok untuk development, bencana untuk production: email 1000 user bikin request timeout. Selalu ganti ke database atau redis sebelum deploy.
queue:listen vs queue:work. listen me-restart framework tiap job (boros, tapi selalu kode terbaru). work daemon yang hemat memori (tapi butuh queue:restart tiap deploy). Untuk production, work + Supervisor adalah standar.
Job gagal diam-diam. Tanpa --tries, job yang error di-retry selamanya dan menumpuk. Set --tries=3 dan pantau failed_jobs. Method failed() di job class adalah tempat mengirim alert ke dirimu.
Catatan teknis: Driver
databasepunya batas: polling tabel tiap detik menambah beban DB, dan tidak mendukung delay presisi di bawah satu menit dengan baik. Saat job mencapai ratusan per menit, saatnya pindah ke Redis (QUEUE_CONNECTION=redis), yang memakai blocking pop sehingga worker idle tanpa membebani apapun dan mendukung rate limiting bawaan.
Tantangan
Email Antrean
Ubah pengiriman WelcomeMail jadi ->queue(). Jalankan queue:work di terminal terpisah, daftar user baru, dan ukur: apakah response register terasa lebih cepat? Cek tabel jobs/f failed_jobs.