Kafka đạt exactly-once nhờ hai cơ chế ghép lại.
1. Idempotent producer (enable.idempotence=true): producer được cấp một producer id và đánh số thứ tự cho từng record trên mỗi partition. Broker loại bỏ bản trùng khi producer retry, đồng thời giữ đúng trình tự. Cấu hình này chống trùng do retry mạng, mặc định bật ở các phiên bản gần đây.
2. Transaction (transactional.id): gói việc ghi nhiều partition + commit offset vào một transaction nguyên tử. Consumer đọc với isolation.level=read_committed chỉ thấy dữ liệu đã commit.
producer.beginTransaction();
producer.send(outputRecord);
producer.sendOffsetsToTransaction(offsets, groupMetadata); // offset in the same txn
producer.commitTransaction();Đây chính là điều làm nên exactly-once của Kafka Streams: đọc → xử lý → ghi kết quả và offset cùng lúc, không có kẽ hở giữa hai bước.
Giới hạn phải nói rõ:
- Chỉ đúng trong phạm vi Kafka. Ngay khi consumer ghi ra DB, gọi API thanh toán hay gửi email, transaction của Kafka không bao trùm hành động đó. Không có gì đảm bảo hai hệ thống cùng commit.
- Chi phí: thêm vòng phối hợp với transaction coordinator, throughput giảm và độ trễ tăng, read_committed khiến consumer chờ tới khi transaction đóng.
- Không cứu được lỗi phía ứng dụng: gửi hai lần từ hai transactional.id khác nhau vẫn ra hai bản ghi.
Kết luận thực dụng: dùng exactly-once cho pipeline nội bộ Kafka-to-Kafka; với side effect ra ngoài, quay lại at-least-once + idempotent bằng khoá nghiệp vụ.