payments · 25 phút
Thanh toán đã commit nhưng client nhận timeout
Thiết kế API retry, consumer chống trùng và luồng đối soát khi trạng thái chưa rõ.
Câu hỏi phỏng vấn
Khách hàng trả 2 triệu đồng. Cơ sở dữ liệu commit thành công, nhưng client timeout trước khi nhận phản hồi. Client gửi lại request. Sau đó, broker phát lại event mà consumer đã xử lý. Hãy thiết kế hệ thống sao cho một ý định thanh toán chỉ tạo một tác động tài chính.
Dành 5 phút để nêu invariant, phân biệt điều client biết với điều backend biết, rồi vẽ transaction boundary cho API và consumer.
Gợi ý 1 · Phạm vi của key
Xác định ai tạo key, key có hiệu lực trong phạm vi nào, và làm gì khi cùng key đi kèm payload khác.
Gợi ý 2 · Hai điểm chống trùng
Đặt API retry và event redelivery vào hai transaction riêng. Xem mỗi transaction cần unique constraint nào và ACK xảy ra ở đâu.
Đáp án mẫu
API dùng unique constraint trên phạm vi nghiệp vụ và idempotency key. Transaction ghi payment, request fingerprint, kết quả và outbox cùng nhau. Replay cùng payload trả kết quả đã lưu. Cùng key với payload khác trả xung đột. Publisher có thể gửi lại event nếu bị dừng sau khi publish nhưng trước khi cập nhật outbox.
Consumer lưu event ID đã xử lý trong cùng transaction với bút toán và ACK sau commit. Đặt thêm invariant nghiệp vụ trên bút toán nếu hai event ID khác nhau có thể đại diện cùng tác động. Khi provider chưa xác nhận, giữ reference gốc và đưa vào đối soát. Theo dõi độ tuổi giao dịch pending, số lần replay và chênh lệch giữa sổ nội bộ và báo cáo đối tác.
Follow-up bằng tiếng Anh
The provider may have processed the payment, but its status endpoint is down. What do you tell the customer, and when would you retry?