# Distribusi Distributed Job Queue Menggunakan Redis dan Worker Pool

> Diterbitkan oleh Literally Web ID pada 21 Oct 2026
> Sumber: https://literally.web.id/articles/distribusi-distributed-job-queue-menggunakan-redis-dan-worker-pool

---

Dalam 15 menit ke depan, kita akan merakit stack antrean tugas latar belakang dari nol sampai berjalan mulus. Pengalaman di lapangan membuktikan bahwa mengandalkan cron job standar atau database relasional untuk memproses jutaan webhook dan email notifikasi adalah resep bencana menuju *race condition* dan beban CPU yang melonjak tajam. Mari kita bangun sistem antrean terdistribusi yang handal menggunakan Redis dan Worker Pool tanpa basa-basi.

## 1. Menu Kita Hari Ini & Alat yang Perlu Disiapkan

Bayangkan kita sedang mengelola sistem e-commerce berskala besar saat *flash sale*. Ribuan pesanan masuk bersamaan, dan kita harus mengirimkan email konfirmasi, memotong stok inventaris, serta memicu webhook pihak ketiga secara asinkron tanpa membuat antarmuka web melambat atau mengalami *time out*.

Untuk menyelesaikannya, kita memerlukan beberapa bahan utama di dapur infrastruktur kita:
- **Redis**: Sebagai penyimpanan data in-memory berkecepatan tinggi yang mendukung operasi atomik.
- **Go (Golang)** atau **Node.js**: Sebagai bahasa pemrograman untuk worker yang mengeksekusi *job*.
- **Supervisor**: Sebagai proses manager yang memastikan *worker pool* kita tetap hidup (*daemonized*) meskipun terjadi *crash*.
- **Linux (Ubuntu/Debian)**: Sebagai lingkungan sistem operasi tempat dapur kita beroperasi.

> [!IMPORTANT]
> Pastikan Redis Anda sudah dikonfigurasi dengan kebijakan memori yang tepat (misal: `volatile-lru` atau `noeviction`) agar tidak terjadi kehilangan data *job* penting saat RAM penuh.

Mari kita bedah peta jalannya agar kita tahu persis apa yang sedang kita rakit sebelum menyalakan mesin.

## 2. Peta Jalan Singkat: Apa yang Sedang Kita Rakit

Kita tidak akan menulis kode teoritis yang rumit. Perjalanan kita hari ini dibagi menjadi tiga pilar utama arsitektur sistem:
1. **Anatomi Atomic Pop/Push di Redis**: Bagaimana memastikan tidak ada *job* yang diproses dua kali oleh *worker* berbeda menggunakan struktur data list dan perintah atomik seperti `RPOPLPUSH` atau `BLMOVE`.
2. **Manajemen Worker dengan Supervisor**: Mengatur sekumpulan proses *worker* di latar belakang agar berjalan paralel, melakukan *auto-restart*, dan mudah diskalakan.
3. **Strategi Dead Letter Queue (DLQ)**: Menangani *job* yang gagal berulang kali agar tidak menyumbat antrean utama dan masuk ke kotak penampungan khusus untuk analisis debug.

Mari kita mulai meracik fondasi pertamanya di terminal Anda.

## 3. Langkah 1: Mempersiapkan Fondasi & Environment

Pertama-tama, pastikan layanan Redis dan Supervisor sudah terpasang dengan baik di server Anda. Buka terminal Anda dan jalankan perintah instalasi berikut jika belum tersedia:

```bash
sudo apt update
sudo apt install redis-server supervisor -y
```

Setelah instalasi selesai, pastikan layanan Redis berjalan secara otomatis saat *booting* dan periksa statusnya:

```bash
sudo systemctl enable redis-server
sudo systemctl start redis-server
sudo systemctl status redis-server
```

> [!TIP]
> Perhatikan baris `Active: active (running)` pada respons terminal. Itu adalah tanda pertama bahwa jantung penyimpanan kita sudah berdenyut.

Selanjutnya, mari kita buat direktori kerja untuk aplikasi *worker* kita di `/var/www/job-worker` dan siapkan skrip dasar.

## 4. Langkah 2: Meracik Konfigurasi Inti Tanpa Basa-basi

Di sinilah inti keajaiban terjadi. Kita akan menggunakan Redis untuk menangani operasi *Push* dan *Pop* secara atomik. Untuk menghindari *race condition* di mana dua *worker* mengambil *job* yang sama secara bersamaan, kita memanfaatkan perintah `BLMOVE` (atau `RPOPLPUSH` pada versi Redis lama) yang memindahkan elemen dari antrean utama ke antrean *processing* secara atomik.

Berikut adalah contoh implementasi *worker* menggunakan Node.js (atau Anda bisa menyesuaikannya dengan bahasa favorit Anda):

```javascript
const Redis = require('ioredis');
const redis = new Redis();

const QUEUE_NAME = 'app_jobs';
const PROCESSING_QUEUE = 'app_jobs_processing';
const DLQ_NAME = 'app_jobs_dlq';
const MAX_RETRIES = 3;

async function processJob(jobData) {
    // Simulasi pemrosesan tugas (misal: kirim email / webhook)
    console.log(`Memproses job: ${jobData.id}`);
    if (Math.random() < 0.2) {
        throw new Error('Gagal koneksi ke API eksternal');
    }
}

async function workerLoop() {
    while (true) {
        try {
            // Ambil job secara atomik dari antrean utama ke processing
            let rawJob = await redis.blmove(QUEUE_NAME, PROCESSING_QUEUE, 'RIGHT', 'LEFT', 0);
            let job = JSON.parse(rawJob);
            
            try {
                await processJob(job);
                // Jika sukses, hapus dari processing queue
                await redis.lrem(PROCESSING_QUEUE, 1, rawJob);
                console.log(`Job selesai: ${job.id}`);
            } catch (err) {
                console.error(`Error pada job ${job.id}: ${err.message}`);
                job.retries = (job.retries || 0) + 1;
                
                await redis.lrem(PROCESSING_QUEUE, 1, rawJob);
                
                if (job.retries >= MAX_RETRIES) {
                    // Masukkan ke Dead Letter Queue (DLQ)
                    console.warn(`Job ${job.id} masuk ke DLQ.`);
                    await redis.rpush(DLQ_NAME, JSON.stringify(job));
                } else {
                    // Kembalikan ke antrean utama untuk dicoba lagi
                    await redis.rpush(QUEUE_NAME, JSON.stringify(job));
                }
            }
        } catch (e) {
            console.error('Redis connection error:', e.message);
            await new Promise(resolve => setTimeout(resolve, 1000));
        }
    }
}

workerLoop();
```

Simpan skrip di atas sebagai `/var/www/job-worker/worker.js`. Skrip ini secara elegan menangani kegagalan, menghitung percobaan ulang, dan memindahkan *job* yang rusak ke *Dead Letter Queue*.

## 5. Langkah 3: Sentuhan Akhir Keamanan & Optimasi

Agar *worker* kita berjalan secara kontinu di latar belakang dan otomatis bangkit jika server mengalami *restart* atau prosesnya *crash*, kita serahkan manajemennya kepada Supervisor.

Buat file konfigurasi baru untuk Supervisor di `/etc/supervisor/conf.d/job-worker.conf`:

```ini
[program:job-worker]
command=node /var/www/job-worker/worker.js
autostart=true
autorestart=true
user=www-data
numprocs=4
process_name=%(program_name)s_%(process_num)02d
redirect_stderr=true
stdout_logfile=/var/log/job-worker.log
```

Perhatikan baris `numprocs=4`. Ini berarti Supervisor akan secara otomatis menjalankan 4 instance *worker* secara paralel (*worker pool*), memaksimalkan pemanfaatan inti CPU server Anda.

Muat ulang konfigurasi Supervisor dan jalankan *worker pool* kita:

```bash
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start job-worker:*
```

## 6. Tes Rasa: Memastikan Semuanya Berjalan Sempurna

Saatnya menguji racikan kita. Mari masukkan contoh *job* manual ke dalam antrean Redis menggunakan klien CLI Redis:

```bash
redis-cli LPUSH app_jobs '{"id": 101, "task": "send_welcome_email", "email": "user@example.com"}'
```

Sekarang, pantau log *worker* secara real-time untuk melihat bagaimana tugas tersebut langsung disambar dan diproses:

```bash
tail -f /var/log/job-worker.log
```

Jika Anda melihat baris log yang mencetak proses dan penyelesaian `job: 101`, itu tanda bahwa sistem antrean terdistribusi Anda sudah hidup dan berjalan dengan sempurna!

## 7. Pertolongan Pertama: Jika Terjadi Kendala Kecil

Di lapangan, hal-hal tak terduga bisa terjadi. Berikut contekan cepat saat menghadapi masalah umum:
- **Worker tidak merespons / Stuck**: Periksa apakah ada proses Node.js yang menggantung menggunakan `ps aux | grep node`. Hentikan paksa dan restart Supervisor dengan `sudo supervisorctl restart job-worker:*`.
- **Job menumpuk di Processing Queue**: Jika *worker* tiba-tiba mati mendadak saat memproses, *job* mungkin tertinggal di `app_jobs_processing`. Anda bisa memindahkannya kembali secara manual ke antrean utama menggunakan perintah Redis `RPOPLPUSH app_jobs_processing app_jobs`.
- **Koneksi Redis ditolak**: Pastikan konfigurasi `bind` di `/etc/redis/redis.conf` mengizinkan koneksi lokal dan port `6379` tidak diblokir oleh firewall internal.

Dengan arsitektur ini, sistem Anda kini siap melahap jutaan tugas latar belakang dengan stabil, aman, dan tanpa rasa khawatir.