Preview Bản nháp chờ review — title, nội dung, link có thể sửa trước khi bỏ nhãn.

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”

  1. Redelivery path đã test chưa (kill worker giữa job)?
  2. Side-effect ngoài DB có key/dedup không?
  3. Publish có outbox (hoặc tương đương) không?
  4. Timeout client có an toàn retry không?
  5. 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.