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:
| Field | Isi |
|---|---|
id | angka unik, berurutan dari 1 |
jenis | nama handler, misal kirim-email atau buat-laporan |
data | object JSON dari client |
status | menunggu, diproses, selesai, atau gagal |
percobaan | berapa kali pekerjaan sudah diambil oleh worker |
hasil | nilai balik handler, terisi setelah selesai |
pesan | pesan 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:
| Method | URL | Aksi | Sukses | Galat |
|---|---|---|---|---|
POST | /pekerjaan | Masukkan pekerjaan baru ke antrean | 201 + header Location | 400, 422 |
GET | /pekerjaan | Daftar pekerjaan, bisa disaring ?status= | 200 | 422 |
GET | /pekerjaan/{id} | Detail satu pekerjaan | 200 | 404 |
GET | /statistik | Hitungan pekerjaan per status | 200 | — |
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:
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 sungguhanPerilaku 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:
node src/demo.mjsOutput yang Dihasilkan:
[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:
PORT=3000 node src/cli.jscurl -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/statistikServer 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:
- Bagian 1 — Antrean dan API: membangun
src/antrean.jsdansrc/pemroses.js— struktur pekerjaan, barisan FIFO, perpindahan status, validasi input, sertabuatServer(pemroses)yang menerima pekerjaan lewat HTTP meski worker belum menyala. - Bagian 2 — Worker: menghidupkan
src/pekerja.js— handler sebagai unit kerja, loop pengambilan pekerjaan, mekanisme bangunkan saat antrean diisi, peristiwaPekerjaanSelesaidanPekerjaanGagal, serta kontrakselesai()untuk menunggu tanpa timer. - Bagian 3 — Retry dan Kegagalan: menyempurnakan jalur gagal — penghitungan percobaan, batas maksimal, penjadwalan ulang ke ujung barisan, dan penangkapan
error.messagesebagai pesan terakhir pekerjaan. - 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.
Rancang struktur job, antrean, dan API pemasukan job.
Buka → # Bagian 2: WorkerProses job secara berurutan dengan worker dan event progress.
Buka → # Bagian 3: Retry dan KegagalanUlangi job gagal dengan backoff dan catat job yang menetap gagal.
Buka → # Bagian 4: Pemantauan dan PengujianPantau statistik antrean dan uji seluruh siklus hidup job.
Buka →