Part 08 · Kafka · 8.1.09

Delivery semantics, transactions và schema evolution

“Exactly once” luôn có scope và assumptions. Kafka transaction có thể atomically publish records và consumed offsets trong Kafka; nó không tự bao phủ database, email, object storage hay payment gateway.

Interview rule: khi nói delivery semantics, luôn nêu boundary, failure model, retry behavior, visibility của consumer và external side effects. Không dùng nhãn guarantee đứng một mình.

1. At-most-once, at-least-once và exactly-once scope

SemanticsCheckpoint timingTrade-off
At-most-onceCommit/advance trước processing hoặc không retryKhông duplicate trong scope, có thể mất work
At-least-onceProcess rồi commit; retry uncertaintyKhông mất trong assumptions, có thể duplicate
Exactly-once processingInput progress + output effects atomic trong supported boundaryBoundary/config/application contract phức tạp hơn

Transport delivery và business effect là hai lớp. Một record chỉ xuất hiện một lần ở consumer API không chứng minh charge/email/database mutation xảy ra đúng một lần; downstream retry và crash vẫn tạo uncertainty.

2. Producer idempotence và transaction fencing

Idempotent producer dùng producer ID, epoch và per-partition sequence để broker loại retries trùng trong session. Nó không deduplicate cùng business event được application publish lại với producer identity mới.

Transactional producer có stable transactional.id; coordinator fence zombie producer có epoch cũ. Một transaction có thể publish tới nhiều partitions và atomically commit consumer offsets cho consume-transform-produce flow. Transaction timeout và unique instance ownership phải phù hợp deployment.

producer.beginTransaction();
try {
  for (record of inputBatch) producer.send(transform(record));
  producer.sendOffsetsToTransaction(offsets, groupMetadata);
  producer.commitTransaction();
} catch (error) {
  producer.abortTransaction();
  throw error;
}

3. Consumer isolation và transaction visibility

Consumer isolation.level=read_committed chỉ trả transactional records đã commit và bỏ aborted records; nó đọc tới last stable offset, nên apparent lag có thể tăng khi transaction mở lâu. read_uncommitted có thể thấy records chưa có kết quả transaction cuối.

Mọi consumers cần cùng visibility contract. Non-transactional records vẫn được đọc bình thường. Transactions lớn/lâu giữ visibility và tăng coordinator/log overhead; batch theo latency/recovery budget thay vì gom vô hạn.

4. External database và side effects

Kafka transaction không phải distributed transaction với DB. Ghi DB rồi commit Kafka offset vẫn có duplicate window; commit offset trước DB vẫn có loss window. Chọn inbox/idempotency constraint, transactional outbox, state-store/stream processor boundary hoặc reconciliation.

Idempotency key phải gắn business operation và được enforce atomically với side effect. Với non-idempotent external API, truyền key downstream nếu hỗ trợ; nếu outcome unknown, query/reconcile trạng thái thay vì retry mù.

EN interview answer · Exactly-once scope

Exactly-once is always scoped. Kafka transactions can provide exactly-once processing for Kafka reads and writes when configured correctly, but an external database call or payment side effect is outside that boundary. I use an idempotency key, inbox/outbox, or another atomic coordination strategy.

5. Ordering, retries và partition keys

Kafka bảo toàn order trong partition theo log order, không toàn topic. Business thường cần per aggregate/account/order ordering, nên partition key phải ổn định. Multiple in-flight requests và retry configuration không tương thích với idempotence có thể reorder; modern clients ràng buộc settings nhưng vẫn phải kiểm version/config.

Consumer parallelism trong cùng partition cũng có thể hoàn tất out of order. Nếu business cần sequence, xử lý serial per key/partition hoặc dùng per-entity version để reject/stash stale events. Global ordering thường đổi throughput/availability quá lớn và hiếm khi thật sự cần.

6. Schema evolution, replay và compatibility gates

Event là contract lâu dài vì retention, replay và consumers deploy độc lập. Add optional field/default thường an toàn hơn remove/rename/change type/meaning. Consumer nên chịu unknown fields; producer không được tái sử dụng field với semantics mới.

Review checklist: guarantee có scope; transaction owner/fencing rõ; read isolation thống nhất; external effect duplicate-safe; ordering key có domain rationale; compatibility/replay được test; schema change có consumer inventory và rollback.
Tài liệu: Kafka Delivery Semantics · Transactions · transactional.id · Consumer isolation.level · Protocol Compatibility