# Membangun Distributed Job Scheduler Skala Ratusan Ribu Eksekusi per Menit

> Diterbitkan oleh Literally Web ID pada 21 Oct 2026
> Sumber: https://literally.web.id/articles/membangun-distributed-job-scheduler-skala-ratusan-ribu-eksekusi-per-menit

---

Dalam 15 menit ke depan, kita akan merakit sebuah sistem penjadwalan tugas terdistribusi yang mampu menangani ratusan ribu eksekusi per menit dari nol sampai berjalan mulus. Mari kita mulai perjalanan ini langsung dari dapur produksi.

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

Pernahkah Anda mendapati server produksi jebol karena cron job berjalan ganda di tiga node berbeda secara bersamaan? Dulu, saya mengandalkan crontab standar di server berbasis Linux, mengarahkannya ke skrip PHP atau Node.js, dan berharap yang terbaik. Masalah muncul ketika autoscaling melahirkan 10 instance baru di balik Nginx dan Load Balancer. Alhasil, laporan bulanan dikirimkan 10 kali ke email klien, dan webhook pembayaran dieksekusi berulang-ulang hingga saldo pihak ketiga terkuras.

Hari ini kita tidak akan mengulangi kesalahan itu. Kita akan meracik distributed job scheduler menggunakan Go, Redis untuk Redlock pattern, PostgreSQL untuk persistence state, dan Nginx sebagai reverse proxy untuk dashboard pemantauan. Siapkan terminal Anda, pastikan Go 1.22+ dan Redis terpasang.

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

Kita akan membangun fondasi yang kokoh secara bertahap. Pertama, kita bedah anatomi kegagalan cron klasik agar kita paham mengapa pendekatan konvensional mati di skala besar. Kedua, kita implementasikan distributed locking menggunakan Redis agar tidak ada job yang tumpang tindih meskipun dijalankan oleh ratusan worker node secara serentak. Ketiga, kita merancang mekanisme failover dan worker recovery.

> [!TIP]
> Jangan lewatkan langkah konfigurasi Redis di bawah ini, karena akurasi waktu milidetik sangat bergantung pada latensi jaringan dan konfigurasi persistence Redis.

## 3. Langkah 1: Mempersiapkan Fondasi & Environment

Mari kita siapkan direktori kerja dan unduh dependencies yang kita butuhkan. Buka terminal Anda dan jalankan perintah berikut:

```bash
mkdir -p /opt/dist-scheduler/bin
cd /opt/dist-scheduler
go mod init scheduler
go get github.com/go-redis/redis/v8
go get github.com/lib/pq
```

> [!IMPORTANT]
> Pastikan koneksi ke Redis memiliki latensi di bawah 2 milidetik jika Anda menargetkan ratusan ribu eksekusi per menit. Latensi Redis adalah leher botol utama arsitektur ini.

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

Sekarang kita tulis kode inti untuk menangani distributed lock menggunakan algoritma Redlock sederhana di Go. Buat file `main.go` di direktori proyek Anda:

```go
package main

import (
	"context"
	"fmt"
	"github.com/go-redis/redis/v8"
	"time"
)

func acquireLock(ctx context.Context, rdb *redis.Client, lockKey string, ttl time.Duration) (bool, string) {
	val := fmt.Sprintf("%d", time.Now().UnixNano())
	succ, err := rdb.SetNX(ctx, lockKey, val, ttl).Result()
	if err != nil || !succ {
		return false, ""
	}
	return true, val
}

func releaseLock(ctx context.Context, rdb *redis.Client, lockKey, val string) {
	// Lua script untuk memastikan kita hanya menghapus lock milik kita sendiri
	script := `if redis.call("get", KEYS[1]) == ARGV[1] then return redis.call("del", KEYS[1]) else return 0 end`
	rdb.Eval(ctx, script, []string{lockKey}, val)
}

func main() {
	rdb := redis.NewClient(&redis.Options{
		Addr: "localhost:6379",
	})

	ctx := context.Background()
	lockKey := "job:lock:generate-report"
	
	acquired, token := acquireLock(ctx, rdb, lockKey, 10*time.Second)
	if acquired {
		fmt.Println("Lock didapat! Menjalankan tugas berat...")
		// Simulasi pekerjaan
		time.Sleep(2 * time.Second)
		releaseLock(ctx, rdb, lockKey, token)
		fmt.Println("Tugas selesai, lock dilepas.")
	} else {
		fmt.Println("Tugas sedang dikerjakan oleh node lain. Lewati.")
	}
}
```

Kompilasi kode tersebut dengan `go build -o bin/scheduler main.go`. Perhatikan bagaimana penggunaan Lua script saat melepas lock mencegah race condition fatal.

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

Agar eksekusi berjalan tanpa gangguan dari gangguan jaringan luar atau masalah file descriptor Linux, sesuaikan konfigurasi sistem operasi Anda. Buat file `/etc/security/limits.d/scheduler.conf`:

```nginx
scheduler   soft   nofile   65535
scheduler   hard   nofile   65535
```

Dan pastikan Nginx yang bertindak sebagai reverse proxy manajemen diproteksi dengan header keamanan yang memadai:

```nginx
server {
    listen 443 ssl http2;
    server_name scheduler.internal.local;

    ssl_certificate /etc/letsencrypt/live/scheduler.internal.local/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/scheduler.internal.local/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}
```

## 6. Tes Rasa: Memastikan Semuanya Berjalan Sempurna

Jalankan dua instance binary secara bersamaan di terminal berbeda untuk melihat Redlock beraksi:

```bash
./bin/scheduler &
./bin/scheduler &
```

Anda akan melihat satu instance sukses mengeksekusi tugas sementara instance kedua mendeteksi lock aktif dan melewatinya dengan mulus tanpa konflik.

## 7. Pertolongan Pertama: Jika Terjadi Kendala Kecil

Jika Anda mendapati lock tidak kunjung lepas karena worker mengalami crash mendadak, pastikan parameter TTL (`time.ToLive`) pada fungsi `acquireLock` diatur secara wajar (misalnya 10-30 detik). Jangan pernah membuat TTL terlalu besar agar proses recovery tidak memakan waktu lama jika worker mati mendadak.