Sistem queue menyimpan pekerjaan sampai ada consumer yang bisa memprosesnya. Aturan delivery, retention, dan recovery-nya menentukan sistem mana yang cocok untuk sebuah aplikasi.
Mulai dari perilaku yang dibutuhkan. Sebuah job mungkin milik satu worker, sementara sebuah event mungkin butuh lima consumer yang independen. Sebagian consumer perlu me-replay message sebelumnya. Recovery juga penting: worker bisa crash setelah kartu di-charge tapi sebelum acknowledgment message-nya.
Kebutuhan-kebutuhan ini menentukan desain queue sekaligus desain consumer-nya.
Dulu saya menaruh library background job, message broker, event log, dan workflow engine dalam satu kategori. BullMQ, PgBoss, RabbitMQ, Kafka, SQS, SNS, ActiveMQ, IBM MQ, Redis Streams, NATS, dan Temporal semuanya jadi "queues" di catatan saya. Label itu menyembunyikan perbedaan model delivery, history, dan koordinasi mereka.
Job, broker, stream, dan workflow
Pertama-tama saya memisahkan kebutuhannya ke dalam empat kategori.
graph TD
A[Something needs to happen later] --> B{What kind of later?}
B --> C[Background job]
B --> D[Message between services]
B --> E[Replayable event history]
B --> F[Long running workflow]
C --> C1[PgBoss or BullMQ]
D --> D1[RabbitMQ, ActiveMQ, IBM MQ, SQS, Pub/Sub, Service Bus]
E --> E1[Kafka, Redis Streams, NATS JetStream]
F --> F1[Temporal]
Background job biasanya dimiliki satu aplikasi. Kirim email. Resize gambar. Retry webhook. Generate laporan.
Message broker biasanya soal komunikasi antar sistem. Payment ngasih tahu fulfillment bahwa sebuah order udah dibayar. Inventory menerima sebuah command. Fraud service dapat salinan sebuah event transaksi.
Event stream adalah history dari fakta-fakta. Consumer baru bisa me-replay-nya. Consumer yang udah ada bisa memproses dengan kecepatannya masing-masing. Kafka ada di sini.
Workflow menyimpan state dari sebuah proses bisnis. Dia bisa menunggu berhari-hari, me-retry operasi, menerima signal, dan melanjutkan dari state yang tersimpan. Temporal mendukung jenis pekerjaan ini.
Kategori-kategori ini membantu menjelaskan perbedaan produknya. BullMQ mengelola background job, RabbitMQ me-routing message, Kafka menyimpan event, SNS mendistribusikan notifikasi, dan Temporal mengoordinasikan workflow.
Pertanyaan yang Saya Tanyakan Duluan
Kalau ada yang nanya "which queue should we use?" biasanya saya mau jawaban untuk pertanyaan-pertanyaan ini dulu:
- Apakah pekerjaan ini internal di satu aplikasi, atau komunikasi antar service?
- Apakah satu consumer menangani tiap message, atau banyak consumer harus melihatnya?
- Apakah kita butuh replay, atau message harus hilang setelah diproses?
- Apakah kita butuh ordering? Kalau iya, ordering berdasarkan apa?
- Apakah pemrosesan bisa terjadi dua kali tanpa merusak bisnis?
- Apakah kita butuh delayed job, cron job, prioritas, rate limit, atau workflow?
- Apakah kita mau mengoperasikan infrastruktur sendiri, atau bayar cloud provider untuk melakukannya?
- Apakah ini sistem greenfield, atau kita sedang berintegrasi dengan teknologi enterprise yang lebih lama?
Sistem yang udah ada mungkin sudah pakai ActiveMQ, IBM MQ, JMS, Beanstalkd, atau Gearman. Riwayat operasional, integrasi, dan persetujuan organisasinya memengaruhi biaya penggantiannya.
Delivery dan retention
Model ini menjaga kategori-kategorinya tetap jelas:
graph LR
P[Producer] --> JQ[Job Queue]
JQ --> W1[Worker]
JQ --> W2[Worker]
P2[Producer] --> EX[Broker or Topic]
EX --> Q1[Consumer Queue A]
EX --> Q2[Consumer Queue B]
P3[Producer] --> LOG[Event Log]
LOG --> CG1[Consumer Group A]
LOG --> CG2[Consumer Group B]
Di jalur pertama, worker berebut job. Satu job biasanya ditangani oleh satu worker.
Di jalur kedua, broker me-routing message. Consumer yang berbeda bisa menerima salinan yang berbeda tergantung binding atau subscription-nya.
Di jalur ketiga, event tetap ada di log selama periode retention tertentu. Consumer melacak posisinya sendiri.
Aturan delivery dan retention memberi tiap kategori tujuan yang berbeda.
PgBoss
PgBoss adalah tool yang langsung terlintas di kepala saya kalau aplikasinya udah punya PostgreSQL dan pekerjaannya background processing biasa.
Perilakunya sangat konkret. Sebuah job adalah sebuah row di Postgres. Worker meng-claim row pakai database locking, biasanya dengan ide yang sama seperti FOR UPDATE SKIP LOCKED. Satu worker me-lock sebuah job. Worker lain melewatinya dan mengambil job lain. Job bergerak melewati state seperti created, active, completed, failed, atau expired.
Transactional enqueue adalah fitur yang berguna. Kamu bisa membuat data bisnis dan meng-enqueue job dalam database transaction yang sama. Kalau transaction-nya di-rollback, job-nya nggak pernah muncul.
Contohnya, sebuah aplikasi mungkin meng-insert order, meng-enqueue process-order, lalu me-rollback insert order tadi. Kalau queue-nya ada di luar database, worker bisa menerima job untuk order yang nggak ada. Kamu bisa menyelesaikan itu dengan outbox pattern, tapi PgBoss ngasih versi yang lebih simpel kalau Postgres udah jadi pusat sistemnya.
sequenceDiagram
participant App
participant DB as PostgreSQL
participant Worker
App->>DB: BEGIN
App->>DB: INSERT order
App->>DB: INSERT job into pgboss table
App->>DB: COMMIT
Worker->>DB: Claim job with row lock
Worker->>DB: Mark job completed
PgBoss cocok untuk kebutuhan ini:
- Kamu udah menjalankan PostgreSQL.
- Volume job-nya sedang.
- Kamu mau komponen yang lebih sedikit.
- Kamu peduli dengan transactional enqueue.
- Kamu suka bisa memeriksa job pakai SQL.
Batasan operasional PgBoss:
- Setiap job adalah beban write ke database.
- Job yang completed dan failed butuh disiplin retention.
- Worker memakai koneksi database.
- Throughput ekstrem bukan tempatnya di sini.
- Dia bukan service broker serbaguna.
Saya menulis lebih banyak soal sisi PostgreSQL-nya di PgBoss vs BullMQ, dan PgBoss juga yang membawa saya ke PostgreSQL advisory locks.
BullMQ
BullMQ menyimpan background job di Redis untuk aplikasi Node.js. Fiturnya mencakup delayed job, retry, backoff, prioritas, rate limit, repeatable job, flow, dan dashboard.
Di dalamnya, BullMQ pakai struktur data Redis. Job yang waiting, active, delayed, completed, failed, lock, dan event tinggal di list, sorted set, hash, dan struktur Redis lainnya. Perubahan yang terdiri dari beberapa langkah pakai Lua script supaya Redis menerapkannya secara atomik.
Worker mengambil sebuah job, memindahkannya ke active, dan terus memperbarui lock selama dia bekerja. Kalau worker-nya mati dan lock-nya berhenti diperbarui, BullMQ bisa menandai job itu sebagai stalled dan mengembalikannya untuk worker lain.
sequenceDiagram
participant Producer
participant Redis
participant Worker
participant Checker as Stall Checker
Producer->>Redis: Add job
Worker->>Redis: Move job to active and lock it
Worker->>Redis: Renew lock while processing
Note over Worker: Worker crashes
Checker->>Redis: Lock expired?
Checker->>Redis: Move job back to waiting
BullMQ cocok untuk kebutuhan ini:
- Redis udah ada di production.
- Pengambilan job dengan latency rendah itu penting.
- Kamu butuh prioritas, delay, retry, rate limit, atau job flow.
- Kamu mau pengalaman job queue Node.js yang solid.
- Kamu memproses job aplikasi dengan volume tinggi.
Batasan operasional BullMQ:
- Durability Redis tergantung setting persistence dan replication-nya.
- Queue berbagi batas memori Redis dengan data lain.
- Payload besar itu ide buruk.
- Atomicity lintas database butuh outbox pattern.
- Dia nggak dimaksudkan jadi RabbitMQ atau Kafka.
Sebelum kamu pakai Redis untuk queue yang durable, cek persistence, failover, memory policy, dan backup-nya. Saya membahas tradeoff yang mirip di strategi caching.
BullMQ mendukung prioritas job. Saya menjelaskan struktur data di baliknya di priority queue.
RabbitMQ
RabbitMQ me-routing message lewat exchange dan queue. Hubungan di antara keduanya adalah topologi broker.
Producer mem-publish ke exchange. Exchange me-routing message ke queue. Consumer membaca dari queue dan meng-acknowledge message. Tipe exchange itu penting: direct, topic, fanout, headers. Binding menentukan queue mana dapat apa.
Model routing itu adalah fitur terbaik RabbitMQ. Satu event payment bisa kamu kirim ke queue email, queue fulfillment, dan queue risk. Kamu bisa me-routing berdasarkan key yang persis, pola topic, atau fanout. Kamu bisa set prefetch supaya consumer cuma menerima sejumlah message yang belum di-acknowledge dalam satu waktu.
graph LR
P[Producer] --> EX[Topic Exchange]
EX -->|payment.succeeded| Q1[Email Queue]
EX -->|payment.*| Q2[Risk Queue]
EX -->|payment.succeeded| Q3[Fulfillment Queue]
Q1 --> C1[Email Worker]
Q2 --> C2[Risk Worker]
Q3 --> C3[Fulfillment Worker]
Durability di RabbitMQ punya lapisan. Exchange-nya bisa durable. Queue-nya bisa durable. Message-nya bisa persistent. Kalau kamu melewatkan salah satunya, setup-nya mungkin kelihatan aman tapi tetap bisa bikin kaget waktu restart.
RabbitMQ cocok untuk kebutuhan ini:
- Kamu butuh routing yang fleksibel.
- Beberapa service meng-consume subset message yang berbeda-beda.
- Kamu mau acknowledgement dan dead letter queue yang matang.
- Kamu butuh request and reply klasik, work queue, atau topic routing.
- Kamu mau semantik broker tanpa harus lompat ke Kafka.
Batasan operasional RabbitMQ:
- Topologinya bisa jadi berantakan.
- Queue yang panjang bisa mengganggu recovery dan performa.
- Dia nggak didesain untuk replay event jangka panjang.
- Clustering dan partition butuh pemahaman operasional yang beneran.
- Ordering jadi rumit kalau ada banyak consumer dan retry.
RabbitMQ cocok kalau kamu butuh broker. Dia cuma jadi beban yang nggak perlu kalau satu-satunya kebutuhan adalah "send this email later."
ActiveMQ dan Artemis
ActiveMQ masih berjalan di banyak sistem, terutama sistem enterprise Java yang lebih lama. Pilihannya ada classic ActiveMQ dan Apache ActiveMQ Artemis, arsitektur broker yang lebih baru.
Mental model-nya biasanya datang lewat JMS: queue, topic, session, acknowledgement, selector, transaction, durable subscription. Queue itu point to point. Topic itu pub/sub. Durable subscription memungkinkan subscriber menerima message topic yang datang selama mereka offline.
Classic ActiveMQ juga masih muncul karena dia mendukung kebutuhan integrasi dan protokol yang lebih lama. Artemis umumnya pilihan yang lebih baik untuk deployment bergaya ActiveMQ yang baru, tapi di dunia nyata keputusannya sering nggak semurni itu. Perusahaannya mungkin udah punya kontrak JMS, service lama, monitoring, runbook, dan orang-orang yang paham failure mode-nya.
ActiveMQ atau Artemis cocok untuk kebutuhan ini:
- Lingkungannya berat di Java atau JMS.
- Sistem yang udah ada sudah bergantung pada semantik JMS.
- Durable subscription itu penting.
- Kompatibilitas protokol itu penting.
- Mengganti broker-nya bakal menciptakan risiko yang lebih besar daripada manfaatnya.
Titik lemahnya:
- Classic ActiveMQ bisa terasa ketinggalan zaman.
- Tuning dan konfigurasi persistence sangat penting.
- Tim web yang lebih baru mungkin belum punya otot operasional untuk mengelolanya.
- Dokumentasinya bisa terasa terpecah antara classic ActiveMQ dan Artemis.
- Dia bukan pilihan alami untuk event stream bergaya analytics.
Saya nggak bakal memilih classic ActiveMQ untuk web app greenfield yang kecil. Saya juga nggak bakal asal mencabutnya dari sistem enterprise yang stabil cuma karena diagram arsitekturnya kelihatan jadul.
IBM MQ
IBM MQ mendukung enterprise messaging, termasuk integrasi perbankan, asuransi, dan mainframe.
IBM MQ berpikir dalam queue manager, queue, channel, listener, persistent message, transaction, dan governance. Queue manager bisa terhubung antar mesin lewat channel. Security dan access control adalah perhatian utama. Di banyak perusahaan, IBM MQ bukan library untuk developer. Dia adalah platform integrasi enterprise dengan proses di sekelilingnya.
IBM MQ cocok untuk kebutuhan ini:
- Kamu berintegrasi dengan mainframe atau sistem enterprise yang umurnya panjang.
- Organisasinya udah menjadikannya standar.
- Governance lebih penting daripada kenyamanan developer.
- Kamu butuh messaging durable yang udah terbukti untuk workflow kritis.
Titik lemahnya:
- Dia berat untuk tim kecil.
- Local development-nya nggak asyik dibandingkan tool modern yang developer first.
- Licensing dan prosesnya bisa memperlambat delivery.
- Dia bukan event stream yang cloud native.
Saya bakal mempertimbangkan IBM MQ kalau integrasi enterprise yang udah ada mengharuskannya. Biaya administrasi dan licensing-nya bakal susah dijustifikasi untuk proyek pribadi yang kecil.
Kafka
Saya mempertimbangkan Kafka kalau consumer perlu menyimpan dan me-replay history event.
Kafka menyimpan record di log append only yang terdistribusi. Producer menulis ke topic, yang berisi partition yang terurut. Consumer membaca partition dan meng-commit offset untuk mencatat progresnya. Membaca sebuah record nggak menghapusnya. Setting retention mengatur berapa lama record tetap ada.
Satu perilaku itu mengubah arsitekturnya. Fraud service, analytics service, notification service, dan pipeline data warehouse semuanya bisa membaca topic yang sama secara independen. Service baru bisa dibuat belakangan dan me-replay event lama. Sebuah bug bisa diperbaiki dan consumer bisa memproses ulang history-nya.
graph LR
P[Payment Service] --> T[Kafka Topic: payments]
T --> CG1[Fraud Consumer Group]
T --> CG2[Analytics Consumer Group]
T --> CG3[Notification Consumer Group]
T --> CG4[Data Warehouse Consumer Group]
Ordering berlaku per partition. Kalau semua event untuk accountId = 123 pakai key yang sama, mereka masuk ke partition yang sama dan menjaga urutan di level account. Kalau kamu mau global ordering, kamu bisa pakai satu partition, tapi konsekuensinya kamu kehilangan paralelisme. Jumlah partition menentukan batas konsumsi paralel ini.
Kafka cocok untuk kebutuhan ini:
- Event-nya adalah fakta bisnis yang layak disimpan.
- Banyak consumer butuh pembacaan yang independen.
- Replay itu penting.
- Throughput-nya tinggi.
- Ordering per key udah cukup.
- Kamu siap dengan partition, schema, offset, dan consumer group.
Batasan operasional Kafka:
- Kompleksitas operasionalnya nyata.
- Dia canggung untuk delayed job yang simpel.
- Desain partition key sangat penting.
- Evolusi schema butuh disiplin.
- Tim kecil bisa overbuild pakai Kafka dengan sangat cepat.
Kafka powerful kalau event adalah bagian dari arsitektur produk. Dia overkill kalau kebutuhannya cuma "run this task later."
SQS
AWS mengoperasikan layanan SQS. Producer mengirim message, SQS menyimpannya, dan worker melakukan polling untuk message yang tersedia.
Perilaku yang penting adalah visibility timeout. Waktu worker menerima sebuah message, SQS nggak menghapusnya. SQS menyembunyikannya selama periode tertentu. Kalau worker-nya selesai, dia menghapus message itu. Kalau worker-nya crash, timeout-nya habis dan message-nya kelihatan lagi.
sequenceDiagram
participant Worker
participant SQS
Worker->>SQS: Receive message
SQS-->>Worker: Message plus receipt handle
Note over SQS: Message is invisible
alt Worker succeeds
Worker->>SQS: Delete message
else Worker crashes
Note over SQS: Visibility timeout expires
SQS-->>Worker: Message can be delivered again
end
SQS Standard ngasih throughput tinggi dan delivery at least once. SQS FIFO ngasih ordering dan batasan deduplication lewat message group, tapi kamu perlu mendesain dengan memperhitungkan model throughput dan grouping-nya.
SQS cocok untuk kebutuhan ini:
- Kamu udah di AWS.
- Kamu mau queue yang durable tanpa harus mengoperasikan broker.
- Worker bisa dibuat idempotent.
- Kamu mau integrasi dengan Lambda, ECS, atau autoscaling.
- Kamu perlu menyerap lonjakan dengan aman.
Batasan operasional SQS:
- Standard queue bisa mengirim duplikat.
- Polling punya tradeoff latency dan biaya.
- Routing-nya sengaja dibuat simpel.
- FIFO queue butuh desain message group yang hati-hati.
- Payload besar butuh S3 atau pola storage lain.
Disiplin utama dengan SQS adalah idempotency. Kalau memproses sebuah message dua kali merusak bisnis, desain consumer-nya belum siap.
SNS
SNS bukan queue. Dia adalah fanout.
Producer mem-publish ke sebuah topic. SNS mendorong message itu ke subscription. Subscription bisa berupa SQS, Lambda, HTTP, email, SMS, dan target lainnya. Pola reliable yang umum adalah SNS topic ke beberapa SQS queue. Setiap consumer memiliki queue, backlog, retry, dan dead letter queue-nya sendiri.
graph LR
P[Publisher] --> SNS[SNS Topic]
SNS --> Q1[SQS: Email Consumer]
SNS --> Q2[SQS: Fraud Consumer]
SNS --> Q3[SQS: Analytics Consumer]
Q1 --> C1[Email Worker]
Q2 --> C2[Fraud Worker]
Q3 --> C3[Analytics Worker]
Subscription filter itu berguna. Kamu bisa mem-publish beberapa tipe event ke satu topic dan membiarkan tiap subscription cuma menerima yang dia pedulikan.
SNS cocok untuk kebutuhan ini:
- Satu event harus memberi notifikasi ke banyak consumer.
- Publisher nggak perlu tahu subscriber-nya.
- Kamu udah di AWS.
- Kamu memasangkannya dengan SQS untuk queue durable per consumer.
Batasan operasional SNS:
- Dia bukan queue kalau berdiri sendiri.
- Replay bukan model normalnya.
- Perilaku delivery tergantung tipe subscriber.
- Koreografi event yang kompleks bisa jadi susah dilacak.
- Kamu jadi terikat ke AWS.
SNS mendistribusikan message yang di-publish ke subscription. Subscription SQS bisa menyimpan salinannya sampai worker memprosesnya.
Redis Streams
Redis Streams ada di antara Redis queue dan event stream yang ringan. Dia bukan Kafka, tapi lebih terstruktur daripada sekadar mendorong job ke Redis list.
Producer menambahkan entry ke sebuah stream. Entry dapat ID. Consumer bisa membaca langsung, atau bergabung ke consumer group. Redis melacak pending message yang udah dikirim tapi belum di-acknowledge. Kalau sebuah consumer mati, consumer lain bisa memeriksa dan meng-claim pending message yang lama.
Redis Streams cocok untuk kebutuhan ini:
- Redis udah ada.
- Kamu mau consumer group tanpa Kafka.
- Kebutuhan retention-nya sederhana.
- Kamu mau stream yang ringan sebelum berkomitmen ke infrastruktur yang lebih besar.
Titik lemahnya:
- Retention memakai memori Redis.
- Batas operasionalnya adalah batas Redis.
- Ekosistemnya lebih kecil daripada Kafka.
- Replay history yang besar banget bukan keunggulannya.
Saya melihat Redis Streams sebagai langkah tengah yang berguna. Dia bagus kalau Kafka bakal kebanyakan, tapi queue biasa nggak cukup.
Google Pub/Sub
Google Pub/Sub adalah versi managed dari GCP untuk event delivery bagi banyak tim. Publisher menulis ke topic. Subscription menerima dari topic. Tiap subscription punya delivery state sendiri, jadi beberapa subscriber bisa meng-consume topic yang sama secara independen.
Subscription bisa berbasis pull atau push. Message butuh acknowledgement. Kalau sebuah message belum di-acknowledge sebelum deadline, dia bisa dikirim lagi. Ordering tersedia lewat ordering key, tapi kamu harus mendesain untuk itu.
Google Pub/Sub cocok untuk kebutuhan ini:
- Kamu di GCP.
- Kamu mau event delivery yang managed.
- Kamu butuh subscription yang independen.
- Delivery push atau pull itu berguna.
- Kamu nggak mau mengoperasikan Kafka.
Titik lemahnya:
- Dia bikin kamu terikat ke cloud provider.
- Delivery at least once berarti idempotency tetap penting.
- Ordering butuh desain key yang eksplisit.
- Rasanya nggak kayak topologi RabbitMQ.
Kalau AWS punya SNS plus SQS, tim GCP sering memilih Pub/Sub sebagai event backbone default.
Azure Service Bus
Azure Service Bus adalah broker managed dari Azure. Dia punya queue, topic, subscription, message lock, session, duplicate detection, dan dead lettering.
Perilaku receive-nya mirip secara semangat dengan queue reliable lainnya. Consumer menerima dan me-lock sebuah message. Kalau dia menyelesaikan message itu, message-nya dihapus. Kalau lock-nya expired sebelum selesai, message-nya bisa dikirim lagi.
Session mengelompokkan message dengan session ID yang sama, yang membantu kalau kamu butuh penanganan terurut per account, per customer, atau per workflow.
Azure Service Bus cocok untuk kebutuhan ini:
- Kamu udah di Azure.
- Kamu butuh queue dan topic yang managed.
- Session membantu model ordering kamu.
- Dead lettering dan duplicate detection itu penting.
- Kamu mau fitur broker yang lebih banyak daripada queue dasar.
Titik lemahnya:
- Keterikatannya ke Azure kuat.
- Throughput dan tier fitur butuh perencanaan.
- Dia bukan replay ala Kafka.
- Konsepnya lebih banyak daripada job queue yang simpel.
Untuk perusahaan yang berat di Azure, ini bisa jadi jawaban yang praktis, walaupun hype-nya lebih sedikit daripada Kafka.
NATS
NATS menyediakan messaging antar service dengan routing berdasarkan subject.
Core NATS adalah pub/sub berbasis subject. Publisher mengirim message ke subject seperti payments.created. Subscriber mendengarkan subject yang persis atau pola wildcard. Core NATS pada dasarnya bukan sistem storage yang durable.
JetStream menambahkan persistence, stream, consumer, acknowledgement, replay, dan retention. Itu memindahkan NATS ke ranah messaging dan streaming yang lebih durable, sambil tetap terasa lebih kecil daripada Kafka di banyak deployment.
NATS cocok untuk kebutuhan ini:
- Kamu butuh messaging service to service yang cepat.
- Kamu suka routing berbasis subject yang simpel.
- Kamu mau footprint operasional yang kecil.
- JetStream ngasih durability yang cukup untuk kasus kamu.
Titik lemahnya:
- Core NATS nggak durable secara default.
- JetStream menambah konsep yang perlu kamu pelajari.
- Kafka punya ekosistem stream processing yang lebih besar.
- Dia kurang umum di stack web app pada umumnya.
NATS cocok untuk sistem yang butuh messaging ringan berbasis subject. Dia bukan default untuk setiap workload.
Beanstalkd dan Gearman
Beanstalkd dan Gearman adalah sistem job yang lebih lama. Mereka mewakili era background processing yang lebih simpel, jadi biasanya saya nggak bakal memilih mereka untuk sistem kritis yang baru hari ini.
Beanstalkd punya tube. Producer memasukkan job ke tube. Worker me-reserve job, memprosesnya, lalu melakukan delete, release, bury, atau touch. Job yang di-reserve punya time to run. Kalau worker-nya nggak selesai, job-nya bisa kembali.
Gearman lebih mirip function dispatch. Client mengirim pekerjaan untuk sebuah function bernama, dan worker yang terdaftar untuk function itu mengeksekusinya.
Tool-tool ini cocok untuk kebutuhan ini:
- Kamu memelihara sistem yang udah ada dan sudah memakainya.
- Workload-nya simpel dan risikonya rendah.
- Kamu butuh job server yang kecil banget.
- Semua orang paham batas durability-nya.
Batasan operasional:
- Ekosistem modernnya lebih kecil.
- Dukungan managed service-nya lebih sedikit.
- Fitur observability dan routing-nya lebih sedikit.
- Lebih cepat kekecilan dibandingkan SQS, BullMQ, RabbitMQ, atau Kafka.
Saya bakal mempertahankan mereka kalau stabil dan membosankan. Saya bakal hati-hati kalau mau memperkenalkan mereka dari awal.
Temporal
Temporal mengelola workflow. Tim sering mempertimbangkannya ketika worker queue mereka harus mengoordinasikan beberapa langkah, retry, dan waktu tunggu.
Temporal menyimpan history workflow secara durable. Kode workflow mendeskripsikan prosesnya. Activity melakukan side effect seperti memanggil API atau mengirim email. Timer, retry, signal, dan state yang berjalan lama adalah konsep kelas satu.
Perbedaan besarnya adalah Temporal mengingat prosesnya. Worker bisa crash, hidup lagi, dan melanjutkan. Workflow bisa tidur berhari-hari. Persetujuan manusia bisa datang belakangan sebagai signal. Kamu nggak harus menyembunyikan semua state itu di payload job dan tabel custom.
Temporal cocok untuk kebutuhan ini:
- Prosesnya berlangsung beberapa menit, jam, atau hari.
- Prosesnya punya beberapa langkah bisnis.
- Kamu butuh timer yang durable.
- Retry butuh state.
- Kamu sedang membangun workflow, bukan menjalankan satu job.
Titik lemahnya:
- Dia bukan pengganti queue yang simpel.
- Aturan workflow yang deterministik butuh waktu belajar.
- Dia menambah beban konseptual dan operasional.
- Job kecil yang cuma satu langkah nggak butuh Temporal.
Aturan saya simpel: kalau worker-nya mulai berubah jadi state machine, saya melirik Temporal.
Kesalahan yang Saya Waspadai
Kesalahan pertama adalah memilih Kafka karena kedengarannya scalable. Kafka luar biasa kalau replay dan history event itu penting. Dia berat kalau kamu cuma perlu mengirim email setelah signup.
Kesalahan kedua adalah pura-pura delivery at least once berarti perilaku exactly once. Kebanyakan sistem ini bisa mengirim sebuah message lebih dari sekali. Consumer butuh idempotency key, write ke database yang aman, dan pemanggilan API eksternal yang hati-hati.
Kesalahan ketiga adalah melupakan dead letter queue. Retry itu bagus sampai ada poison message yang di-retry selamanya. Message yang gagal butuh tempat untuk dituju dan cara untuk diperiksa.
Kesalahan keempat adalah bilang "we need ordering" tanpa menyebut cakupannya. Global ordering itu mahal. Ordering per account itu realistis. Ordering per user itu umum. Tanpa ordering pun sering kali nggak masalah.
Queue bisa menyerap kenaikan traffic sementara. Dia nggak bisa terus-terusan menutupi kapasitas consumer yang kurang. Kalau producer membuat 10.000 message per detik dan consumer memproses 1.000, backlog-nya bakal terus tumbuh.
Metrik yang saya mau di queue serius mana pun adalah queue depth, umur message tertua, processing rate, jumlah retry, jumlah dead letter, dan error rate worker.
Default Saya
Untuk web app biasa, saya mulai dengan PgBoss kalau Postgres udah ada dan workload-nya sedang. Ini menjaga arsitekturnya tetap kecil.
Kalau Redis udah jadi infrastruktur production yang reliable dan aplikasinya butuh background job yang cepat, saya pakai BullMQ.
Untuk decoupling service di AWS, SQS adalah default saya. Saya menambahkan SNS kalau muncul kebutuhan fanout.
Untuk routing broker klasik antar service, RabbitMQ masih sangat masuk akal.
Untuk sistem enterprise Java, saya mempertimbangkan ActiveMQ atau Artemis. Untuk integrasi enterprise yang teregulasi, saya mempertimbangkan IBM MQ. Pengalaman operasional yang udah ada bisa menjustifikasi pilihan-pilihan ini.
Untuk event bisnis yang bisa di-replay dan pipeline dengan throughput tinggi, Kafka adalah opsi yang serius, tapi baru setelah menerima biaya operasionalnya.
Untuk workflow bisnis yang butuh state persisten di beberapa langkah, saya mengevaluasi Temporal.
Tabel Ringkasan
| Sistem | Apa Itu | Paling Cocok Untuk | Kelebihan | Kekurangan |
|---|---|---|---|---|
| PgBoss | Job queue berbasis PostgreSQL | Background job yang harus ikut menikmati keamanan database transaction | Stack simpel, durable, transactional enqueue, bisa diperiksa pakai SQL | Menambah beban database, dibatasi throughput Postgres, bukan broker |
| BullMQ | Job queue berbasis Redis | Background job Node.js yang cepat dengan delay, retry, prioritas, dan flow | Cepat, developer experience yang bagus, fitur job yang lengkap | Durability dan memori Redis butuh operasional yang serius, bukan untuk replay |
| RabbitMQ | Message broker klasik | Messaging antar service dengan routing, acknowledgement, dan dead letter queue | Exchange yang fleksibel, perilaku broker yang matang, model work queue yang bagus | Nggak ada replay jangka panjang, topologi dan clustering butuh disiplin |
| ActiveMQ / Artemis | Broker enterprise bergaya JMS | Sistem enterprise Java dan kompatibilitas protokol legacy | Model JMS yang familiar, queue dan topic, durable subscription | Classic ActiveMQ bisa terasa ketinggalan zaman, tuning dan pengetahuan broker itu penting |
| IBM MQ | Messaging untuk integrasi enterprise | Mainframe, perbankan, asuransi, messaging lintas sistem yang teregulasi | Sangat matang, reliable, governance dan dukungan transaction yang kuat | Berat, mahal, kurang ramah untuk tim kecil |
| Kafka | Event log terdistribusi | Event bisnis yang bisa di-replay, pipeline analytics, stream dengan throughput tinggi | Replay, consumer group, throughput tinggi, ekosistem yang kuat | Kompleks secara operasional, canggung untuk job simpel, desain partition itu penting |
| SQS | Queue AWS yang managed | Worker queue AWS yang durable dan decoupling service | Managed, durable, elastis, model visibility timeout yang simpel | Delivery at least once, routing terbatas, tradeoff polling |
| SNS | Fanout AWS yang managed | Satu event dikirim ke banyak subscriber, biasanya lewat SQS queue | Fanout simpel, filtering, decoupling publisher | Bukan queue kalau berdiri sendiri, nggak ada replay normal, terikat ke AWS |
| Redis Streams | Stream Redis yang ringan | Consumer group dan replay sederhana kalau Redis udah ada | Cepat, native Redis, recovery lebih baik daripada list biasa | Retention berbasis memori, ekosistem lebih kecil, bukan skala Kafka |
| Google Pub/Sub | Pub/sub GCP yang managed | Event delivery GCP dengan subscription yang independen | Managed, delivery push atau pull, fanout gampang | Terikat ke GCP, idempotency wajib, ordering butuh desain |
| Azure Service Bus | Broker Azure yang managed | Enterprise messaging Azure dengan queue, topic, session, dan dead letter | Fitur broker managed, session untuk ordering, duplicate detection | Terikat ke Azure, lebih kompleks daripada queue simpel, bukan replay ala Kafka |
| NATS | Messaging ringan berbasis subject | Messaging internal antar service yang cepat, dengan JetStream untuk durability | Cepat, subject yang elegan, footprint kecil | Core NATS nggak durable, ekosistem lebih kecil daripada Kafka |
| Beanstalkd / Gearman | Job server simpel yang lebih lama | Memelihara sistem job kecil yang udah ada | Simpel, ringan, mental model yang gampang | Durability, routing, observability, dan dukungan managed modern yang terbatas |
| Temporal | Workflow engine yang durable | Proses bisnis yang berjalan lama dengan retry, timer, dan state | Workflow yang durable, retry yang stateful, recovery dari crash | Bukan queue simpel, determinisme workflow butuh waktu belajar |

Memuat komentar...