Slide vendor: exactly-once delivery. Incident thật: user bị charge hai lần sau redelivery / timeout retry.
Mạng + crash + timeout ⇒ hệ phân tán không có phép màu “giao đúng một lần” miễn phí. Điều bạn cần là effect xảy ra một lần (hoặc an toàn nếu hai lần) — công thức đời thực gần như luôn là:
at-least-once delivery × idempotent processing (+ outbox khi publish)
≈ “exactly-once effect” theo nghĩa thực dụng
Recovery playbook đã giả định idempotent handler — đây là vì sao.
Ba lớp hay nhầm một từ
Delivery — broker/consumer nhận message mấy lần?
Processing — handler function chạy mấy lần?
Effect — side-effect (charge, row, email) xuất hiện mấy lần?
- EOS marketing hay nói delivery/processing trong một hệ hẹp (Kafka transactions, v.v.) — vẫn không thay idempotent business logic khi bạn gọi Stripe / gửi SMS / ghi bảng ngoài transaction.
- Bạn (và kế toán) care effect.
At-least-once là mặc định
- Visibility / lease: worker chết → message hiện lại.
- Ack mất / process kill sau side-effect trước ack → redelivery.
- Client timeout + retry = lần hai có chủ đích hoặc không.
Thiết kế như redelivery sẽ xảy ra — vì nó sẽ.
Idempotent processing
- Dedup store / unique constraint theo key scope.
- Natural idempotent: “set state = X” thay “increment” mù.
- Handler đọc “đã xử lý messageId?” trước side-effect đắt.
Publish: dual-write vs outbox
db.save(); bus.publish(); chết giữa hai dòng → mất
event hoặc orphan event.
Outbox ghi event cùng
transaction; publisher at-least-once đọc outbox + consumer
idempotent.
Xem
dual-write vs outbox.
Unknown timeout
Không biết lần 1 đã effect chưa → uncertain. Retry không key = có thể double. Đúng: tra cứu / cùng idempotency key / “create if not exists”.
Checklist “claim EOS”
- Redelivery path đã test chưa (kill worker giữa job)?
- Side-effect ngoài DB có key/dedup không?
- Publish có outbox (hoặc tương đương) không?
- Timeout client có an toàn retry không?
- Inbox/dedup multi-instance (không chỉ Map in-memory một pod)?
Một câu để nhớ
Exactly-once effect ≈ at-least-once delivery × idempotent
processing.
Chữ trên slide broker không thay unique constraint.