Masalah utama yang dihadapi adalah worker Ant JS runtime baru menimbulkan deadlock sehingga antre job membengkak dan request API mengalami timeout. Artikel ini segera memaparkan gejala nyata, log metrik, dan penyebab teknis yang bisa ditindaklanjuti.

Gejala nyata dan tolok ukur awal

Setelah migrasi worker queue ke Ant JS runtime, sistem menunjukkan tiga gejala utama: timeout API (>30 detik), throughput worker turun, dan antre job di dashboard queue terus menumpuk meski tidak ada lonjakan request.

Log dan metrik yang mendukung

Contoh log dari observability stack:

2024-10-02T10:22:10Z ERROR queue-worker timeout=30000ms jobId=order:123 state=processing ant-thread=worker-3

Grafik latency memperlihatkan lonjakan latency dari 120ms menjadi >1.2 detik; concurrency worker tetap tinggi namun tidak ada penyelesaian job. Prometheus menunjukkan ant_worker_active stabil sementara ant_worker_blocked meningkat.

Replikasi minimal deadlock

Gunakan contoh worker Ant JS yang memproses job berat.

import { Worker } from 'antjs/worker';

const worker = new Worker(async ({ queue, job }) => {
  await queue.fetchJob();
  // blocking synchronous loop
  for (let i = 0; i < 1e8; i++) {
    Math.sqrt(i);
  }
  await job.complete();
});

worker.run();

Loop sinkron seperti di atas akan menahan event loop Ant JS jika tidak dijalankan di thread pool khusus atau dibagi menjadi chunk asinkron. Kombinasi blocking dan worker pool default menyebabkan deadlock saat resource CPU penuh.

Analisis akar masalah

Ant JS menggunakan runtime berbasis event loop dengan worker pool terbatas. Deadlock terjadi karena:

  • Worker blocking: job CPU-bound dijalankan langsung tanpa offloading ke worker pool, menyumbat thread Ant JS dan mencegah job lain dieksekusi.
  • Isolasi I/O tidak memadai: operasi database/Redis heavy mengunci koneksi pool dan menunggu penanganan dari thread yang sama, menciptakan antrian meluas.
  • Event loop starvation: tidak ada pengembalian control sampai job selesai, sehingga heartbeat atau timeout tidak pernah diperbarui.

Profiling dan stack trace

Gunakan profiler Ant JS (atau V8 sampling) untuk menangkap stack trace saat deadlock. Contoh trace:

WorkerThread.run
  -> processOrder (CPU-bound)
    -> syncMathLoop
    -> await db.save()

Trace menunjukkan job tetap di syncMathLoop tanpa yield, dan panggilan db.save() tidak pernah menyelesaikan karena tidak ada event loop cycle baru.

Strategi perbaikan

1. Konfigurasi worker pool: tingkatkan ukuran pool thread Ant JS untuk menampung job blocking, namun jangan berlebihan karena akan menambah context switch. Gunakan ANT_WORKER_THREADS atau konfigurasi serupa untuk menyesuaikan jumlah worker dengan jumlah job dan CPU.

2. Isolasi blocking: pindahkan operasi CPU-bound ke worker thread tersendiri dengan Ant.worker.spawn atau integrasi dengan native worker. Jika memungkinkan, pecah job menjadi chunk asinkron:

for (let chunk = 0; chunk < 1e8; chunk += 1e6) {
  await Ant.yield();
  processChunk(chunk);
}

3. Observability: pasang metrik latency per job, blocking time, dan antre queue secara real-time. Gunakan OpenTelemetry untuk trace end-to-end agar dapat melihat antre job mana yang memicu blocking.

4. Fallback jika Ant di-throttle: siapkan route fallback yang memindahkan job kritis ke runtime Node.js tradisional ketika Ant worker saturasi. Terapkan circuit breaker pada queue dispatcher yang memantau rasio timeout/throughput, lalu alihkan job ke worker cadangan.

Checklist verifikasi pasca perbaikan

  • Periksa log bahwa timeout tidak lagi terjadi dan job tuntas dalam SLA.
  • Monitoring menampilkan antre job stabil atau menurun.
  • Profiling menunjukkan event loop memiliki waktu idle yang cukup; blocking time turun.
  • Penerapan worker pool baru berjalan tanpa error konfigurasi.
  • Fallback worker diuji dengan simulasi throttle dan berhasil menyelesaikan job kritis.

Dengan mengikuti langkah-langkah ini, deadlock worker di Ant JS runtime dapat dihentikan, throughput kembali normal, dan timeout API dapat dihindari.