Tiga Semantik
| Semantik | Artinya | Cara mencapainya |
|---|---|---|
| At-most-once | Boleh hilang, tidak boleh dobel | Commit offset sebelum memproses |
| At-least-once | Tidak boleh hilang, boleh dobel | Commit offset sesudah memproses — default yang sehat |
| Exactly-once | Tepat sekali | Transaksi Kafka, atau at-least-once + pemrosesan idempoten |
Apa yang Sebenarnya Dijamin Transaksi Kafka
Transaksi Kafka membuat rangkaian baca dari Kafka → proses → tulis ke Kafka → commit offset menjadi satu operasi atomik. Semuanya berhasil, atau semuanya dibatalkan.
produser.initTransactions()
produser.beginTransaction()
produser.send(topik_keluaran, hasil1)
produser.send(topik_keluaran, hasil2)
produser.sendOffsetsToTransaction(offset, metadataGrup) ← kuncinya di sini
produser.commitTransaction()
Offset konsumen di-commit di dalam transaksi yang sama dengan penulisan hasil. Karena itu tidak mungkin hasil tertulis tanpa offset maju, atau sebaliknya.
Batas jaminannya adalah Kafka itu sendiri. Bila pemrosesan Anda menulis ke PostgreSQL, memanggil API pembayaran, atau mengirim email, transaksi Kafka tidak dapat membatalkan efek samping tersebut. Aplikasi bisa crash setelah email terkirim tetapi sebelum transaksi di-commit, dan email itu akan terkirim lagi saat pesan diproses ulang.
Untuk efek samping di luar Kafka, jawabannya bukan transaksi Kafka melainkan pemrosesan idempoten: kunci deduplikasi, upsert, atau tabel pesan-terproses.
Sisi Konsumen: isolation.level
isolation.level=read_uncommitted ← default: pesan transaksi terlihat sebelum commit isolation.level=read_committed ← hanya pesan dari transaksi yang sudah di-commit
Produser transaksional yang rapi menjadi sia-sia bila konsumen hilirnya memakai default read_uncommitted — konsumen itu tetap membaca pesan dari transaksi yang kemudian dibatalkan. Kedua sisi harus dikonfigurasi bersama.
Biayanya
- Latensi bertambah — konsumen
read_committedhanya boleh membaca sampai batas transaksi yang stabil, sehingga pesan tertahan sampai transaksi ditutup. - Throughput turun — tiap transaksi menambah penulisan marker dan koordinasi.
- Kompleksitas naik —
transactional.idharus stabil dan unik per instans produser, termasuk setelah restart dan penjadwalan ulang pod.
Transaksi Kafka bersinar pada alur Kafka ke Kafka: agregasi, penggabungan aliran, pengayaan data. Kafka Streams bahkan mengaktifkannya cukup dengan processing.guarantee=exactly_once_v2. Untuk alur yang berujung pada basis data atau API eksternal, rancang idempotensi — itu lebih sederhana dan lebih jujur.
Pola Praktis yang Sering Lebih Baik
At-least-once + kunci deduplikasi di basis data:
INSERT INTO pesanan_terproses (id_pesan, diproses_pada)
VALUES ('evt-abc-123', now())
ON CONFLICT (id_pesan) DO NOTHING
RETURNING id_pesan;
-- tidak ada baris kembali → pesan sudah pernah diproses, lewati
Pola ini bekerja lintas sistem, mudah dipahami saat insiden, dan tidak menuntut koordinasi khusus dari Kafka.