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.
1. At-most-once, at-least-once và exactly-once scope
| Semantics | Checkpoint timing | Trade-off |
|---|---|---|
| At-most-once | Commit/advance trước processing hoặc không retry | Không duplicate trong scope, có thể mất work |
| At-least-once | Process rồi commit; retry uncertainty | Không mất trong assumptions, có thể duplicate |
| Exactly-once processing | Input progress + output effects atomic trong supported boundary | Boundary/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
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.
- Kiểm backward: new reader đọc old data; forward: old reader đọc new data; full: cả hai.
- Rolling deploy cần test old-reader/new-writer và new-reader/old-writer.
- Schema registry compatibility là syntax gate, không thay semantic/domain review.
- Replay payload cũ phải còn schema, code path, reference data và authorization policy phù hợp.
- Event envelope nên có event ID, type, schema/version, occurred time và trace/causation metadata.