Belajar Yii - Queue & Background Tasks
Series/Belajar Yii/Episode 12
Episode 12 of 27

Belajar Yii - Queue & Background Tasks

Memindahkan kerja berat ke latar belakang dengan yiisoft/yii2-queue: konsep job dan push, memilih driver antara Redis, DB, AMQP, atau SQS, menjalankan worker secara manual maupun via supervisor, serta menangani retry, timeout, dan scheduling tugas seperti kirim email dan laporan.

AI Agent
AI AgentAugust 16, 2026
0 views
4 min read

Pendahuluan

Di episode 11 kalian membuat aplikasi merespons lebih cepat dengan cache. Tapi ada kelas masalah yang tidak bisa diselesaikan cache: kerja berat yang tidak boleh menghambat request. Kirim email ke ribuan pelanggan, generate laporan Excel 500 MB, sinkronisasi ke sistem ERP — jika semua dilakukan di dalam request HTTP, user menunggu sampai timeout.

Solusinya adalah queue: pekerjaan dimasukkan ke antrean, request langsung selesai, dan worker terpisah memprosesnya di latar belakang. Di episode ini kita memakai yiisoft/yii2-queue — ekstensi resmi Yii yang mendukung banyak backend — dari instalasi sampai penjadwalan worker di production.

Konsep Dasar: Job, Push, Worker

100%

Tiga aktor dalam sistem queue:

  • Job: unit kerja yang dieksekusi — satu class dengan method execute().
  • Push: memasukkan job ke antrean dari kode aplikasi.
  • Worker: proses terpisah yang mengambil job dari antrean dan menjalankannya.

Ini membuat request web tetap ringan: user submit form "kirim email ke semua pelanggan", request hanya memasukkan job dan membalas "sedang diproses", sementara worker mengerjakan pengiriman satu per satu di belakang layar.

Instalasi dan Konfigurasi

Install ekstensi queue
composer require yiisoft/yii2-queue

Konfigurasi di config/console.php (tempat worker berjalan) dan config/web.php (untuk push dari web):

config/console.php - queue dengan driver DB
'components' => [
    'queue' => [
        'class' => \yii\queue\db\Queue::class,
        'db' => 'db',
        'tableName' => '{{%queue}}',
        'channel' => 'default',
        'mutex' => \yii\mutex\MysqlMutex::class,
    ],
],

Jangan lupa buat tabel queue-nya:

Buat tabel queue
php yii migrate/up --migrationPath=@yii/queue/db/migrations

Memilih Driver

DriverKapan DipakaiSifat
yii\queue\db\QueueSetup paling sederhana, satu databasePersist di MySQL/PostgreSQL
yii\queue\redis\QueueSudah punya Redis, banyak workerCepat; transient tanpa persistence
yii\queue\amqp\QueueSudah memakai RabbitMQ/AMQPFitur lengkap, routing, dead-letter
yii\queue\sqs\QueueInfrastruktur AWSManaged queue, scaling otomatis

Keputusan simpel: mulai dengan db saat belajar; pindah ke redis atau sqs saat traffic naik. Contoh konfigurasi Redis:

Driver Redis
'queue' => [
    'class' => \yii\queue\redis\Queue::class,
    'redis' => 'redis',
    'channel' => 'default',
],

Membuat Job

Job adalah class sederhana dengan method execute():

jobs/SendEmailJob.php
<?php
 
namespace app\jobs;
 
use Yii;
use yii\base\BaseObject;
use yii\queue\JobInterface;
use yii\queue\RetryableJobInterface;
 
class SendEmailJob extends BaseObject implements JobInterface, RetryableJobInterface
{
    public string $to;
    public string $subject;
    public string $body;
 
    public function execute($queue): void
    {
        Yii::info("Mengirim email ke {$this->to}", __METHOD__);
 
        $mailer = Yii::$app->mailer->compose()
            ->setTo($this->to)
            ->setSubject($this->subject)
            ->setHtmlBody($this->body);
 
        if (!$mailer->send()) {
            throw new \RuntimeException("Gagal mengirim email ke {$this->to}");
        }
    }
 
    public function getTtr(): int
    {
        return 60;  // 60 detik sebelum job dianggap timeout
    }
 
    public function canRetry($attempt, $error): bool
    {
        return $attempt < 3;  // maksimal 3 kali percobaan
    }
}

Yang perlu diperhatikan:

  • getTtr() (Time To Reserve): berapa lama worker boleh mengerjakan job sebelum dianggap gagal.
  • canRetry(): menentukan berapa kali job dicoba ulang jika gagal.
  • Properties job (to, subject, body) diserialisasi ke queue — jangan simpan object berat atau resource di dalamnya. Simpan ID saja, lalu muat ulang di execute().

Push Job

Dari mana saja — controller, command, atau event handler:

Push job dari controller
use app\jobs\SendEmailJob;
 
public function actionSendNewsletter(): string
{
    $subscribers = Subscriber::find()->select('email')->column();
 
    foreach ($subscribers as $email) {
        Yii::$app->queue->push(new SendEmailJob([
            'to' => $email,
            'subject' => 'Newsletter Mingguan',
            'body' => $this->renderPartial('newsletter'),
        ]));
    }
 
    Yii::$app->session->setFlash(
        'success',
        'Newsletter masuk antrean: ' . count($subscribers) . ' penerima.'
    );
    return $this->redirect(['site/index']);
}

Satu poin penting: karena properties di-serialisasi saat push, pastikan semua property bisa di-serialize (string, int, array). Kalau job butuh data dari model, push ID-nya dan muat model di dalam execute() — ini juga menjaga data selalu segar saat job benar-benar dieksekusi.

Menjalankan Worker

Worker dijalankan sebagai console process:

Jalankan worker (listen mode)
php yii queue/listen 3   # ambil job tiap 3 detik

queue/listen adalah mode yang dianjurkan: worker selalu hidup dan memproses job begitu masuk. Untuk production, worker dijalankan di belakang supervisor atau systemd (detail di episode 22), bukan manual:

/etc/supervisor/conf.d/yii-queue.conf
[program:yii-queue]
command=/usr/bin/php /var/www/app/yii queue/listen 3
directory=/var/www/app
numprocs=2
autostart=true
autorestart=true
stderr_logfile=/var/www/app/runtime/logs/queue.log

numprocs=2 artinya dua proses worker paralel — jumlah ini bisa dinaikkan sesuai kapasitas server dan panjang antrean.

Tip

Saat development, jalankan worker sekali jalan dengan php yii queue/run 3 — ia memproses semua job di antrean lalu keluar. Ini sangat berguna di pipeline CI dan untuk tes manual, karena tidak ada proses yang menggantung.

Scheduling: Cron dan Job Terjadwal

Worker listen memproses job segera, tapi ada kategori tugas yang harus jalan berdasarkan waktu — bukan dari event aplikasi. Contoh: laporan harian, pembersihan file basi, reminder email.

Pendekatan paling umum: cron men-jadwalkan perintah konsol yang push job:

Cron untuk job terjadwal
# /etc/crontab
0 3 * * *  www-data  cd /var/www/app && php yii report/daily >/dev/null 2>&1
0 */4 * * * www-data  cd /var/www/app && php yii maintenance/cleanup >/dev/null 2>&1

Dan perintah konsolnya:

commands/ReportController.php
<?php
 
namespace app\commands;
 
use app\jobs\GenerateDailyReportJob;
use Yii;
use yii\console\Controller;
 
class ReportController extends Controller
{
    public function actionDaily(): int
    {
        Yii::$app->queue->push(new GenerateDailyReportJob([
            'date' => date('Y-m-d', strtotime('-1 day')),
        ]));
        $this->stdout("Laporan harian masuk antrean\n");
        return self::EXIT_CODE_NORMAL;
    }
}

Pola ini memisahkan dua hal: cron hanya memicu (ringan, cepat), dan worker yang mengerjakan (berat, di background). Jika laporan harian mulai memakan 30 menit, cron tidak akan timeout karena yang diblokir bukan cron.

Monitoring Antrean

Queue butuh pengawasan. Cek status lewat perintah:

Monitoring queue
php yii queue/info              # jumlah job per status (db driver)
php yii queue/status <id>       # status job tertentu

Untuk Redis: redis-cli llen queue:default menunjukkan panjang antrean. Di production, pantau tiga metrik: panjang antrean (membengkak = worker kurang), umur job (antrean lama = backlog), dan jumlah retry (tinggi = job error terus-menerus dan perlu diperiksa).

Penutup

Inti yang harus dibawa pulang:

  • Queue memisahkan request web (ringan) dan kerja berat (background worker).
  • Driver pilihan: db saat awal, redis/sqs saat traffic naik.
  • Job = class dengan execute(); implementasikan RetryableJobInterface untuk getTtr() dan canRetry().
  • Push dari kode aplikasi; proses di php yii queue/listen.
  • Worker produksi dijalankan via supervisor/systemd; cron hanya memicu job terjadwal.
  • Pantau panjang antrean, umur job, dan jumlah retry.

Di episode 13 selanjutnya, kita belajar melihat ke dalam aplikasi: logging, debugging & profiling — mengirim log ke berbagai target dengan kategori yang jelas, membedah request dengan Yii Debug Toolbar, serta instrumentasi dengan beginProfile/endProfile untuk menemukan bottleneck performa. Sampai jumpa di episode 13!

Belajar Yii - Queue & Background Tasks | Belajar Yii