Exactly-once (Kenapa Sulit di RabbitMQ) #
Dalam diskusi delivery guarantee, banyak orang bertanya:
“Apakah RabbitMQ mendukung exactly-once delivery?”
Jawaban jujurnya:
Tidak secara native.
Namun kita bisa mendesain sistem sehingga efek bisnisnya menjadi exactly-once.
Artikel ini membahas apa itu exactly-once, mengapa ia sulit secara teori dalam sistem terdistribusi, dan bagaimana pendekatan praktisnya di RabbitMQ.
“Exactly-once bukan tentang memastikan pesan hanya dikirim satu kali — tetapi memastikan efeknya hanya terjadi satu kali.”
Apa Itu Exactly-Once Delivery? #
Exactly-once berarti:
- Message tidak pernah hilang
- Message tidak pernah diproses dua kali
- Efek bisnis terjadi tepat satu kali
Secara teori, ini adalah jaminan paling kuat.
Secara praktik, ini sangat sulit dicapai dalam sistem terdistribusi.
Mengapa Exactly-Once Sulit? #
Dalam sistem terdistribusi, kita menghadapi:
- Network failure
- Partial crash
- Timeout
- Retry
- Duplicate transmission
Contoh klasik:
1️⃣ Consumer menerima message 2️⃣ Consumer memproses dan update database 3️⃣ Consumer crash sebelum ack 4️⃣ Broker mengirim ulang message
Dari sudut pandang broker:
- Message belum selesai → dikirim ulang
Dari sudut pandang bisnis:
- Proses sudah terjadi sekali
Inilah sumber duplicate.
RabbitMQ Secara Default #
RabbitMQ mendukung:
- At-least-once (manual ack)
- At-most-once (auto ack)
Tidak ada mekanisme native exactly-once seperti transactional stream processing.
Karena itu, exactly-once harus dibangun di atas at-least-once.
Exactly-Once vs Exactly-Once Effect #
Penting membedakan dua hal:
- Exactly-once delivery (sangat sulit)
- Exactly-once effect (realistis)
Kita mungkin tidak bisa mencegah duplicate delivery.
Namun kita bisa memastikan duplicate tidak menghasilkan efek ganda.
Inilah pendekatan praktis.
Strategi Mencapai Exactly-Once Effect #
1️⃣ Idempotent Consumer #
Consumer dirancang agar:
- Aman dipanggil berulang
- Tidak menghasilkan efek ganda
Contoh:
- Update status berdasarkan state machine
- Gunakan upsert
2️⃣ Unique Event ID + Deduplication #
Setiap message memiliki:
- event_id (UUID)
Simpan event_id yang sudah diproses di database.
Jika event_id sama muncul lagi → abaikan.
Ini adalah strategi paling umum.
3️⃣ Database Constraint #
Gunakan:
- Unique constraint
- Primary key
- Insert ignore / upsert
Duplicate otomatis ditolak oleh database.
Database menjadi penjaga idempotency.
4️⃣ Transactional Outbox Pattern #
Untuk memastikan publish dan database commit konsisten:
1️⃣ Simpan event di tabel outbox dalam satu transaksi DB 2️⃣ Background worker publish ke RabbitMQ 3️⃣ Tandai sebagai sent
Ini mencegah:
- Data tersimpan tanpa event
- Event terkirim tanpa data
Outbox membantu sisi producer.
5️⃣ Consumer Side Transaction + Ack Setelah Commit #
Urutan yang benar:
1️⃣ Mulai DB transaction 2️⃣ Proses event 3️⃣ Commit DB 4️⃣ Kirim ack
Jika crash sebelum commit:
- Transaction rollback
- Message dikirim ulang
Jika crash setelah commit sebelum ack:
- Duplicate mungkin terjadi
- Tetapi deduplication akan mencegah efek ganda
Perbandingan Delivery Guarantee #
| Guarantee | Hilang? | Duplicate? | Kompleksitas |
|---|---|---|---|
| At-Most-Once | Bisa | Tidak | Rendah |
| At-Least-Once | Tidak | Bisa | Sedang |
| Exactly-Once Effect | Tidak | Tidak (efeknya) | Tinggi |
Exactly-once membutuhkan desain tambahan.
Hubungan dengan Quorum Queue #
Quorum queue:
- Meningkatkan durability
- Mengurangi risiko kehilangan data karena node crash
Namun quorum tidak otomatis memberi exactly-once.
Duplicate tetap mungkin terjadi karena faktor aplikasi.
Misconception Umum #
“Jika saya menggunakan quorum dan manual ack, berarti exactly-once.”
Tidak.
Manual ack hanya memberi at-least-once.
Exactly-once membutuhkan idempotency dan deduplication.
Kapan Exactly-Once Effect Penting? #
Sangat penting untuk:
- Payment processing
- Financial ledger
- Inventory stock deduction
- Critical state machine
Untuk workload non-kritis, at-least-once biasanya cukup.
Ringkasan #
RabbitMQ tidak menyediakan exactly-once delivery native.
Namun kita bisa mencapai exactly-once effect dengan:
- Idempotent consumer
- Unique event ID
- Deduplication storage
- Database constraint
- Outbox pattern
Exactly-once adalah hasil desain, bukan fitur konfigurasi.
Penutup #
Dalam sistem terdistribusi, exactly-once delivery murni hampir mustahil tanpa tradeoff besar.
Namun exactly-once effect adalah target realistis yang bisa dicapai dengan disiplin desain.
RabbitMQ memberi fondasi at-least-once.
Sisanya adalah tanggung jawab arsitektur aplikasi.
Karena dalam dunia nyata, bukan jumlah pengiriman yang paling penting.
Tetapi jumlah efek bisnis yang benar-benar terjadi.
Dan arsitektur yang matang memastikan efek itu terjadi tepat sekali.