Tidak semua pekerjaan aplikasi harus selesai sebelum response dikirim. Mengirim kwitansi, membuat laporan berkala, atau membersihkan berkas lama boleh menunggu — asal pekerjaannya dicatat, dikerjakan di belakang layar, dan dilaporkan bila gagal. Pada proyek ini kita membangun background job processor yang menangani persis itu: antrean pekerjaan di memori, worker yang mengurainya satu per satu, kebijakan percobaan ulang saat handler gagal, dan endpoint pemantauan — semuanya dengan core module saja, tanpa package tambahan. Proyek ini menerapkan langsung pola EventEmitter dari Chapter Event-Driven Architecture serta pola pabrik server dari Proyek REST API Manajemen Tugas.

Yang Kita Bangun #

Setiap pekerjaan direpresentasikan sebagai satu object dengan bentuk tetap. Client hanya mengirim jenis dan data; seluruh field lainnya diurus pemroses:

FieldIsi
idangka unik, berurutan dari 1
jenisnama handler, misal kirim-email atau buat-laporan
dataobject JSON dari client
statusmenunggu, diproses, selesai, atau gagal
percobaanberapa kali pekerjaan sudah diambil oleh worker
hasilnilai balik handler, terisi setelah selesai
pesanpesan galat terakhir, terisi setelah gagal

Siklus hidup pekerjaan bergerak dari menunggu ke diproses, lalu berakhir di selesai atau gagal; pekerjaan yang gagal tetap di tengah jalan kembali ke menunggu hingga batas percobaan tercapai. Seluruh interaksi berlangsung lewat empat endpoint:

MethodURLAksiSuksesGalat
POST/pekerjaanMasukkan pekerjaan baru ke antrean201 + header Location400, 422
GET/pekerjaanDaftar pekerjaan, bisa disaring ?status=200422
GET/pekerjaan/{id}Detail satu pekerjaan200404
GET/statistikHitungan pekerjaan per status200—

Pemisahan kode galat mengikuti disiplin yang sama dengan proyek sebelumnya: 400 berarti body bukan JSON valid, 422 berarti input tidak lolos validasi (termasuk jenis yang tidak dikenal), 404 berarti resource tidak ada, dan 405 berarti method tidak didukung. Kegagalan handler saat pemrosesan bukan kode HTTP — kegagalan semacam itu tercatat sebagai status pekerjaan gagal, bukan menolak permintaan yang sudah diterima.

Struktur Direktori #

Kode proyek terbagi per tanggung jawab: antrean sebagai penyimpanan, worker sebagai pelaksana, pemroses sebagai perangkai, dan server sebagai pintu HTTP:

text Struktur direktori proyek
background-job-processor/
├── package.json            # type module — semua berkas .js adalah ESM
├── src/
│   ├── antrean.js          # antrean FIFO dan perpindahan status
│   ├── handler.js          # registry jenis pekerjaan + handler simulasi
│   ├── pekerja.js          # worker: loop, percobaan ulang, peristiwa
│   ├── pemroses.js         # perangkai antrean + worker + validasi
│   ├── server.js           # server HTTP dan tabel rute
│   ├── cli.js              # titik masuk produksi (port dari PORT)
│   ├── demo.mjs            # sesi demo utama dalam satu proses
│   ├── demo-antrean.mjs    # demo mekanisme antrean
│   ├── demo-api.mjs        # demo API tanpa worker
│   ├── demo-pekerja.mjs    # demo worker dan peristiwa
│   ├── demo-retry.mjs      # demo percobaan ulang
│   └── demo-pemantauan.mjs # demo statistik lewat HTTP
└── tests/
    ├── antrean.test.js     # unit test antrean
    ├── pekerja.test.js     # unit test worker dan percobaan ulang
    ├── pemroses.test.js    # unit test validasi dan penyusunan
    └── integrasi.test.js   # integration test lewat HTTP sungguhan

Perilaku yang Diharapkan #

Cara termudah melihat perilaku pemroses adalah menjalankan sesi demo utama — satu proses yang menyalakan antrean dan worker, memasukkan empat pekerjaan (termasuk satu yang sengaja gagal), menunggu seluruh antrean terkuras, lalu mencetak keadaan akhir beserta statistiknya:

bash
node src/demo.mjs

Output yang Dihasilkan:

text
[antre] #1 kirim-email {"penerima":"[email protected]","subjek":"Kwitansi pemesanan"}
[antre] #2 buat-laporan {"judul":"Laporan penjualan mingguan"}
[antre] #3 kirim-email {"penerima":"bukan-email","subjek":"Uji jalur kegagalan"}
[antre] #4 kirim-email {"penerima":"[email protected]","subjek":"Pengingat jadwal"}
[selesai] #1 kirim-email -> Email "Kwitansi pemesanan" terkirim ke [email protected]
[selesai] #2 buat-laporan -> Laporan "Laporan penjualan mingguan" selesai dibuat
[selesai] #4 kirim-email -> Email "Pengingat jadwal" terkirim ke [email protected]
[gagal] #3 kirim-email -> Penerima email tidak valid: bukan-email (percobaan: 3)
--- keadaan akhir ---
#1 kirim-email selesai percobaan=1
#2 buat-laporan selesai percobaan=1
#3 kirim-email gagal percobaan=3
#4 kirim-email selesai percobaan=1
statistik: {"menunggu":0,"diproses":0,"selesai":3,"gagal":1,"total":4}

Bacaan singkat output di atas: empat baris [antre] membuktikan urutan masuk, tiga baris [selesai] menunjukkan pekerjaan yang berhasil, dan satu baris [gagal] memperlihatkan pekerjaan dengan penerima tidak valid yang berhenti setelah tiga percobaan. Pada keadaan akhir terlihat pekerjaan #3 berstatus gagal dengan percobaan=3 — bukti kebijakan percobaan ulang bekerja sampai batas, bukan berhenti pada kegagalan pertama.

Untuk pemakaian sungguhan, pemroses dinyalakan lewat titik masuk produksi lalu dihubungi dengan client HTTP apa pun:

bash
PORT=3000 node src/cli.js
bash
curl -X POST 127.0.0.1:3000/pekerjaan -H 'Content-Type: application/json' -d '{"jenis": "kirim-email", "data": {"penerima": "[email protected]", "subjek": "Kwitansi"}}'
curl 127.0.0.1:3000/statistik

Server berjalan sampai dihentikan dengan Ctrl+C; pada saat itu worker menyelesaikan pekerjaan yang sedang berjalan sebelum proses keluar. Seluruh suite test dijalankan dengan node --test dari akar proyek.

Pembagian Empat Bagian #

Pembangunan proyek mengikuti empat tahap yang berdiri di atas tahap sebelumnya:

  1. Bagian 1 — Antrean dan API: membangun src/antrean.js dan src/pemroses.js — struktur pekerjaan, barisan FIFO, perpindahan status, validasi input, serta buatServer(pemroses) yang menerima pekerjaan lewat HTTP meski worker belum menyala.
  2. Bagian 2 — Worker: menghidupkan src/pekerja.js — handler sebagai unit kerja, loop pengambilan pekerjaan, mekanisme bangunkan saat antrean diisi, peristiwa PekerjaanSelesai dan PekerjaanGagal, serta kontrak selesai() untuk menunggu tanpa timer.
  3. Bagian 3 — Retry dan Kegagalan: menyempurnakan jalur gagal — penghitungan percobaan, batas maksimal, penjadwalan ulang ke ujung barisan, dan penangkapan error.message sebagai pesan terakhir pekerjaan.
  4. Bagian 4 — Pemantauan dan Pengujian: menutup dengan endpoint /statistik, saringan ?status=, peristiwa sebagai alat pemantauan, suite test tiga lapis, dan titik masuk produksi yang berhenti dengan rapi.

Setiap bagian menutup satu keputusan desain: bagian 1 memisahkan penyimpanan antrean dari HTTP, bagian 2 memisahkan pelaksanaan dari penyimpanan, bagian 3 mengubah kegagalan dari kejadian yang menghentikan proses menjadi data yang bisa dipantau, dan bagian 4 membuktikan semuanya dari luar sebagaimana client melihatnya. Mari kita mulai dari fondasinya: antreannya sendiri.