L
Cron 🚨 Post-Mortem & Incident Runbook â€ĸ Literally Web ID

Arsitektur Background Worker Skala Besar dengan Distributed Cron & Redis

âąī¸ 15 menit baca â€ĸ 📅 20 Oct 2026 â€ĸ đŸ‘ī¸ 2 views
Arsitektur Background Worker Skala Besar dengan Distributed Cron & Redis
🔊 Dengarkan Artikel Ini

1. Alarm Tengah Malam: Saat Layanan Produksi Mulai Tumbang

Pukul 02:14 WIB, ponsel saya bergetar liar dengan suara sirine PagerDuty yang memekakkan telinga. Dashboard Grafana menyala merah terang, menunjukkan metrik CPU utilization di klaster backend Go melompat ke angka 98% secara serentak di semua node, sementara database MySQL utama kami mengalami lonjakan connection pool exhaustion hingga titik jenuh. Ini bukan sekadar lonjakan trafik pengguna organik, melainkan mimpi buruk setiap infrastruktur engineer: badai eksekusi tugas latar belakang (background worker storm) yang lepas kendali.

Di balik layar, sistem kami mengandalkan penjadwal berbasis cron klasik yang disebar ke dalam sepuluh instance server berbeda demi redundansi. Namun, ketidakhiran mekanisme koordinasi terpusat membuat setiap server mengeksekusi skrip pembersihan data harian yang sama secara bersamaan. Alih-alih berjalan efisien, pekerjaan tersebut memicu race condition parah di tingkat basis data dan mengunci tabel-tabel krusial. Permintaan HTTP dari klien mulai berguguran dengan status HTTP 504 Gateway Timeout, sementara Nginx di lapisan edge kewalahan menerima antrean koneksi.

Situasi semakin runyam ketika antrean pesan di sistem queue internal mulai menumpuk ribuan job yang gagal dieksekusi karena timeout koneksi database. Jika kami tidak segera mengambil tindakan bedah infrastruktur malam itu juga, seluruh ekosistem layanan web kami akan mengalami cascading failure total. Perjalanan panjang menelusuri log sistem pun dimulai dari tumpukan pesan error yang berkedip-kedip di terminal.

2. Jejak Digital: Menelusuri Anomali Log & Lonjakan Metrik

Begitu mengambil alih kendali terminal, langkah pertama saya adalah masuk ke dalam instance server menggunakan SSH dan langsung mengarahkan pandangan ke log supervisor yang mengawasi proses background worker. Perintah tail -f /var/log/supervisor/worker-stderr.log langsung memuntahkan barisan teks merah yang berjalan sangat cepat:

[2024-10-24 02:15:10] ERROR: Job [CleanupExpiredSessions] failed: SQLSTATE[HY000]: General error: 1205 Lock wait timeout exceeded; try restarting transaction
[2024-10-24 02:15:11] WARNING: Worker node-04 retrying job due to unexpected disconnection...
[2024-10-24 02:15:12] CRITICAL: Connection pool exhausted, unable to acquire database connection for worker thread.

Pola yang sama berulang ribuan kali per menit. Saya kemudian membuka terminal terpisah untuk memeriksa status proses aktif menggunakan htop dan melihat bahwa setiap kali cron sistem mentrigger skrip pada menit tertentu, sepuluh container backend di klaster Kubernetes langsung berebut melakukan write operation ke tabel yang sama di MySQL.

[!WARNING] Menjalankan native crontab secara independen di lingkungan multi-server tanpa mekanisme leader election atau distributed locking adalah resep pasti menuju bencana split-brain dan korupsi data.

Untuk membuktikan hipotesis ini, saya memeriksa metrik Redis yang bertindak sebagai caching layer kami. Ternyata, kunci-kunci penanda status eksekusi tidak menggunakan pendekatan atomik yang benar, sehingga kondisi check-then-act gagal total di tengah konkurensi tinggi. Kami butuh solusi arsitektural yang tidak hanya menghentikan pendarahan sesaat, tetapi juga menutup celah logika secara permanen.

3. Membongkar Biang Kerok: Akar Masalah yang Sebenarnya

Setelah menganalisis log dan grafik metrik secara mendalam, akar masalah mengerucut pada tiga hal utama:

  1. Absennya Distributed Lock: Penjadwal tugas berjalan berdasarkan jam sistem lokal masing-masing server, bukan berdasarkan status global terpusat.
  2. Race Condition di Database: Sepuluh worker mencoba memperbarui baris data yang sama secara bersamaan tanpa proteksi transaksi terisolasi yang memadai.
  3. Retry Policy yang Brutal: Konfigurasi Supervisor langsung melakukan restart instan pada proses yang gagal tanpa menerapkan exponential backoff, sehingga memperparah beban database yang sedang sekarat.

[!IMPORTANT] Dalam arsitektur terdistribusi, waktu lokal server tidak pernah bisa diandalkan secara mutlak, dan proses konkuren harus selalu dikoordinasikan melalui mekanisme atomic lock berbasis memori cepat seperti Redis.

Untuk mengatasi masalah ini secara tuntas, kita harus beralih dari model eksekusi buta menuju model Distributed Cron yang dikendalikan oleh Redis menggunakan pola Redlock atau operasi SET NX EX yang aman secara atomik. Mari kita lihat bagaimana implementasi kodenya dalam bahasa Go untuk memastikan hanya ada satu worker yang mengeksekusi tugas pada satu waktu.

package main

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

func AcquireTaskLock(ctx context.Context, client *redis.Client, lockKey string, ttl time.Duration) (bool, string) {
	// Menghasilkan token unik untuk memastikan worker yang memegang lock adalah yang berhak melepaskannya
	token := fmt.Sprintf("%d", time.Now().UnixNano())
	succes, err := client.SetNX(ctx, lockKey, token, ttl).Result()
	if err != nil || !succes {
		return false, ""
	}
	return true, token
}

func ReleaseTaskLock(ctx context.Context, client *redis.Client, lockKey string, token string) {
	// Lua script untuk memastikan penghapusan lock hanya jika token-nya cocok
	script := `
		if redis.call("get", KEYS[1]) == ARGV[1] then
			return redis.del(KEYS[1])
		else
			return 0
		end
	`
	client.Eval(ctx, script, []string{lockKey}, token)
}

Potongan kode di atas memastikan bahwa sebelum sebuah tugas latar belakang dijalankan, worker wajib meminta izin ke Redis. Jika kunci sudah dipegang oleh instance lain, worker tersebut akan dengan elegan membatalkan eksekusinya alih-alih merusak basis data.

4. Tindakan Darurat: Menghidupkan Kembali Layanan dalam Hitungan Menit

Sebelum perubahan kode arsitektur tersebut dapat di-deploy melalui pipeline CI/CD, kita harus memadamkan api yang sedang membakar infrastruktur produksi saat ini juga. Berikut adalah rangkaian perintah emergency runbook one-liner terminal yang saya eksekusi malam itu untuk memulihkan layanan dalam waktu kurang dari 60 detik:

# 1. Hentikan seluruh proses background worker secara paksa di semua node untuk menghentikan beban database
sudo supervisorctl stop all-workers-*

# 2. Masuk ke Redis CLI dan bersihkan semua kunci lock yang macet (stuck locks)
redis-cli --scan --pattern 'lock:cron:*' | xargs -L 1 redis-cli del

# 3. Restart layanan MySQL untuk membersihkan koneksi yang menggantung (hanging threads)
sudo systemctl restart mysql

# 4. Berikan jeda 5 detik, lalu aktifkan kembali worker dengan kapasitas konkurensi dibatasi (rate-limited)
sleep 5 && sudo supervisorctl start worker-group:worker-01

Langkah darurat ini terbukti ampuh. Grafik latensi di Grafana langsung melandai turun dari angka 4 detik menjadi di bawah 120 milidetik. Koneksi basis data kembali normal, dan antrean HTTP mulai terlayani dengan lancar tanpa ada error 504.

5. Kebijakan Retrospektif: Menambal Celah agar Tak Pernah Terulang

Kejadian tengah malam tersebut memberikan pelajaran mahal tentang pentingnya ketahanan infrastruktur. Agar insiden serupa tidak terulang, kami merombak total konfigurasi Supervisor dan manajemen retry policy pada seluruh layanan backend. Berikut adalah file konfigurasi Supervisor yang baru dan telah diperkuat dengan kebijakan backoff dan batas memori yang ketat:

[program:production-background-worker]
command=/usr/local/bin/worker-service --env=production
autostart=true
autorestart=true
startsecs=10
startretries=3
stderr_logfile=/var/log/worker/stderr.log
stdout_logfile=/var/log/worker/stdout.log
user=www-data
stopsignal=TERM
stopwaitsecs=30
# Mencegah memory leak menumpuk dengan membatasi jumlah eksekusi per proses
starthold=true

Selain itu, kami juga membandingkan pendekatan arsitektural lama dan baru untuk memastikan tim memahami perubahannya:

Parameter Pendekatan Lama (Raw Cron) Pendekatan Baru (Distributed Cron + Redis)
Koordinasi Desentralisasi murni (Setiap server jalan sendiri) Terpusat via Redis Lock atomik (SET NX)
Risiko Race Condition Sangat Tinggi (Sering terjadi deadlock) Nol (Hanya satu leader yang mengeksekusi)
Manajemen Kegagalan Langsung restart tanpa jeda (Brutal) Exponential Backoff & Circuit Breaker
Skalabilitas Klaster Menyebabkan lonjakan beban linier Konstan dan aman meskipun menambah 100 node

Transisi ini memastikan bahwa penambahan kapasitas server di masa depan tidak akan pernah lagi melipatgandakan beban pada basis data relasional.

6. Pasang Alarm: Otomasi Deteksi Dini untuk Tim On-Call

Langkah pertahanan terakhir yang tidak kalah penting adalah memastikan sistem memantau dirinya sendiri sebelum manusia sempat panik melihat pager. Kami memasang aturan alerting Prometheus dan Alertmanager yang memantau kesehatan worker pool secara real-time.

Berikut adalah konfigurasi aturan Prometheus (PromQL) untuk mendeteksi ketika worker mengalami kegagalan beruntun atau ketika Redis lock gagal dilepaskan dalam jangka waktu tertentu:

groups:
  - name: background_worker_alerts
    rules:
      - alert: WorkerQueueStuck
        expr: redis_key{key=~"lock:cron:.*"} > 0 and rate(worker_job_failures_total[5m]) > 0.5
        for: 2m
        labels:
          severity: critical
        annotations:
          summary: "Background worker task is stuck or failing repeatedly"
          description: "Instance {{ $labels.instance }} has detected persistent locking failures for critical background jobs."

Dengan sistem deteksi dini ini, jika ada anomali sekecil apapun pada siklus hidup tugas latar belakang, tim on-call akan langsung mendapatkan notifikasi spesifik sebelum layanan publik terdampak. Arsitektur tangguh bukan berarti tidak pernah rusak, melainkan bagaimana sistem pulih dengan sendirinya dan tidak membiarkan kesalahan yang sama terjadi dua kali.

đŸˇī¸ Tags: #Mysql #Redis #Go #Prometheus #Supervisor

Apakah panduan tutorial ini membantu Anda?

Masukan Anda sangat berharga untuk terus memperbarui dan menyempurnakan kualitas materi teknis kami.

🎉

Terima kasih banyak atas apresiasi Anda!

Tim kami terus menyempurnakan tutorial agar selalu akurat dengan versi teknologi terbaru.

Bantu Kami Menyempurnakan Materi Ini

⚡ Literally Tech Digest

Update Tutorial & Developer Tools Terbaru Langsung ke Email Anda

Dapatkan kurasi artikel arsitektur software, tips DevOps, panduan database, serta rilis fitur developer utility gratis setiap minggu. Bebas spam, bisa berhenti kapan saja.

âŗ Mencari artikel...
Tutorial & Artikel Terkait
Developer Tools
Tidak ditemukan tutorial atau tool dengan kata kunci "".
Ketik minimal 2 karakter untuk mencari artikel atau nama tools.
Tekan ESC untuk menutup Cari di Semua Artikel →