Tiga Tipe Queue di RabbitMQ Modern
| Classic | Quorum | Stream | |
|---|---|---|---|
| Replikasi | Tidak (mirroring sudah dihapus) | Raft, ganjil (3/5) | Ya |
| Pesan dihapus saat dikonsumsi | Ya | Ya | Tidak — log append-only |
| Bisa dibaca ulang | Tidak | Tidak | Ya, lewat offset |
| Antrean sangat panjang | Kurang baik | Baik | Sangat baik |
| Prioritas pesan | Ya | Tidak | Tidak |
| Penghitung percobaan | Tidak | Ya (x-delivery-limit) | — |
| Throughput | Tertinggi | Sedang | Sangat tinggi |
| Latensi | Terendah | Sedikit lebih tinggi | Rendah |
Fitur ha-mode / classic queue mirroring dinyatakan usang sejak 3.8 dan dihapus di RabbitMQ 4.0. Kebijakan lama seperti {"ha-mode":"all"} tidak lagi berfungsi.
Alasan penghapusannya bukan sekadar kerapian: mirroring memakai algoritma replikasi yang bisa kehilangan data secara diam-diam saat partisi jaringan pulih. Quorum queue memakai Raft, yang punya jaminan konsistensi yang jelas dan teruji.
Bila Anda masih menjalankan mirrored queue, migrasi ke quorum queue adalah pekerjaan yang tidak bisa ditunda lebih lama.
Quorum Queue: Default yang Tepat untuk Hampir Semua Hal
await ch.assertQueue('pesanan.email', {
durable: true, // WAJIB untuk quorum
arguments: {
'x-queue-type': 'quorum',
'x-quorum-initial-group-size': 3, // jumlah replika
'x-delivery-limit': 5, // penghitung percobaan bawaan
'x-dead-letter-exchange': 'pesanan.dlx',
'x-max-length': 500000,
'x-overflow': 'reject-publish'
}
});Cluster 3 node, satu quorum queue:
node-1 (leader) node-2 (follower) node-3 (follower)
│ │ │
publish masuk │ │
├──── replikasi ──────>│ │
├──── replikasi ───────────────────────────>│
│<──── ack ────────────┤ │
│ │
2 dari 3 sudah menyimpan = MAYORITAS
│
confirm dikirim ke producer ✓
Bila node-1 mati:
node-2 dan node-3 memilih leader baru dalam beberapa detik.
Tidak ada pesan yang hilang, karena mayoritas sudah menyimpannya.Quorum berarti mayoritas. Dengan angka genap, mayoritasnya tidak menguntungkan:
3 node → mayoritas 2 → tahan 1 node mati ✅
5 node → mayoritas 3 → tahan 2 node mati ✅
2 node → mayoritas 2 → tahan 0 node mati ❌ lebih buruk dari 1 node
4 node → mayoritas 3 → tahan 1 node mati ❌ sama dengan 3 node, biaya lebih besarTiga node adalah titik keseimbangan untuk sebagian besar kebutuhan. Lima dipakai bila Anda benar-benar perlu bertahan dari dua kegagalan bersamaan.
Yang Tidak Dimiliki Quorum Queue
- Prioritas pesan (
x-max-priority) — tidak didukung. Bila benar-benar perlu, pakai queue terpisah per tingkat prioritas, yang umumnya juga lebih mudah dipahami. - TTL per pesan — hanya TTL per queue yang didukung penuh.
- Queue non-durable atau exclusive — quorum queue selalu durable.
Sebagai gantinya, quorum queue memberi x-delivery-limit, yang menghilangkan seluruh kelas bug poison message tanpa kode tambahan apa pun.
Stream: Ketika Anda Butuh Membaca Ulang
await ch.assertQueue('peristiwa.pesanan', {
durable: true,
arguments: {
'x-queue-type': 'stream',
'x-max-length-bytes': 20_000_000_000, // 20 GB
'x-stream-max-segment-size-bytes': 500_000_000,
'x-max-age': '7D'
}
});
// konsumer WAJIB menyebutkan titik mulai baca
await ch.prefetch(100); // wajib untuk stream
await ch.consume('peristiwa.pesanan', handler, {
noAck: false,
arguments: { 'x-stream-offset': 'first' } // first | last | next
// | angka offset | timestamp
});Stream berperilaku seperti log Kafka: pesan tidak dihapus saat dikonsumsi, beberapa konsumer independen bisa membaca dari posisi berbeda, dan data lama dibuang berdasarkan ukuran atau umur, bukan berdasarkan konsumsi.
Butuh pesan tidak boleh hilang saat satu node mati?
→ Quorum queue. (jawaban untuk mayoritas kasus)
Butuh membaca ulang riwayat, atau beberapa konsumer independen
dengan posisi masing-masing?
→ Stream.
Butuh prioritas pesan, atau queue sementara yang hilang saat
koneksi putus (mis. balasan RPC)?
→ Classic queue.
Butuh throughput maksimum dan pesan boleh hilang?
→ Classic queue, transient.Migrasi dari Mirrored ke Quorum
Tipe queue tidak bisa diubah di tempat — queue harus dibuat ulang. Prosedur tanpa kehilangan pesan:
# 1. buat queue baru bertipe quorum, nama berbeda
rabbitmqadmin declare queue name=pesanan.email.v2 durable=true \
arguments='{"x-queue-type":"quorum","x-delivery-limit":5}'
# 2. pasang binding yang sama
rabbitmqadmin declare binding source=pesanan \
destination=pesanan.email.v2 routing_key='pesanan.*.dibuat'
# 3. hentikan konsumer lama; biarkan queue lama dihabiskan
# (atau pindahkan sisanya dengan shovel)
rabbitmqctl set_parameter shovel migrasi '{
"src-protocol":"amqp091","src-uri":"amqp://localhost",
"src-queue":"pesanan.email",
"dest-protocol":"amqp091","dest-uri":"amqp://localhost",
"dest-queue":"pesanan.email.v2",
"src-delete-after":"queue-length"}'
# 4. arahkan konsumer ke queue baru, verifikasi
# 5. hapus binding dan queue lama
rabbitmqadmin delete binding source=pesanan \
destination_type=queue destination=pesanan.email \
properties_key='pesanan.*.dibuat'
rabbitmqadmin delete queue name=pesanan.emailAlternatif yang lebih rapi bila Anda bisa menerima jeda singkat: setel default_queue_type = quorum, hentikan producer sebentar, habiskan queue lama, hapus, lalu biarkan aplikasi mendeklarasikan ulang saat start.