Đang tải nội dung…
Đang tải nội dung…
PAID đến trước CREATED và cách một projection tự quay ngược trạng thái
Kafka Streams, CDC & Event Processing · Phần 3Bối cảnh Fortex là hư cấu. Hai record và kết quả bên dưới được tạo trong Docker Lab. Chúng chứng minh một lịch chạy cụ thể, không đại diện latency production.
Hà gửi Ben ảnh chụp trang vận hành: order F-882 đang PAID, vài giây sau quay về CREATED. Không message nào mất. Cả hai đều nằm trong Kafka.
Ben mở consumer: projection chỉ lấy record đến sau cùng. “Partition giữ order mà anh?”
Tom không vội trả lời. Anh hỏi hai event được tạo ở đâu. CREATED đến từ order service đang retry sau một đợt nghẽn; PAID đến từ payment service và đã tới Kafka trước. Kafka giữ đúng thứ tự được append trong partition. Nó không đoán được quan hệ nghiệp vụ giữa hai producer.
Processing time chịu ảnh hưởng của queue, retry, pause, rebalance và backpressure. Event time gần nghiệp vụ hơn, nhưng cũng có thể sai vì clock skew, timezone, producer bug hoặc thiết bị offline.
Kafka Streams concepts phân biệt time semantics để ứng dụng chọn timestamp extractor phù hợp. Việc có timestamp trong record không tự giải quyết ordering: ứng dụng vẫn phải định nghĩa record cũ hơn được bỏ, cập nhật hay bù trừ thế nào.
Lab gửi qua Kafka theo thứ tự:
PAID, event time 10 giây, domain version 2.CREATED, event time 5 giây, domain version 1.| Chính sách projection | Trạng thái cuối |
|---|---|
| Record xử lý sau thắng | CREATED |
| Event time lớn hơn thắng | PAID |
| Domain version lớn hơn thắng | PAID |
f06e3fcf-3e43-47c7-8cea-05afd7b6af3dLab không đo throughput. Nó cố tình đảo arrival và business order để kiểm tra invariant: trạng thái không được lùi từ version 2 về version 1.
Nếu order aggregate phát một sequence tăng đơn điệu, consumer có thể cập nhật có điều kiện:
UPDATE order_projection
SET state = :state, version = :version
WHERE order_id = :order_id
AND version < :version;
Đồng hồ lệch 30 giây không phá được rule ấy. Version thể hiện causal order của một aggregate. Nó không phải total order cho toàn hệ thống; version 9 của order A không “sau” version 8 của order B theo nghĩa hữu ích nào.
Nếu nhiều service cùng có quyền đổi state, cần một state machine hoặc một owner phát domain events chuẩn hóa. Lấy timestamp từ hai máy rồi dùng MAX(timestamp) chỉ thay race condition bằng clock condition.
Event time vẫn hữu ích cho analytics, windowing và dữ liệu từ thiết bị offline. Hãy mang theo:
event_id để deduplicate;occurred_at với timezone/precision rõ;source và schema version;Khi chỉ có timestamp, sink có thể dùng (occurred_at, event_id) làm tie-breaker ổn định và đưa conflict không phân giải được sang reconciliation. Đừng âm thầm lấy record đến cuối cùng rồi gọi đó là sự thật.
Trong một partition, Kafka cung cấp log order. Nếu mọi transition của một order đi qua cùng producer logic và cùng key, thứ tự ấy rất có giá trị. Nhưng retry từ outbox khác, import lịch sử, CDC, hai producer hoặc repartition có thể làm event đến không theo thời gian nghiệp vụ.
Tom yêu cầu Ben viết invariant trước khi sửa code:
Với cùng
order_id, projection chỉ nhận transition hợp lệ và version phải tăng.
Sau đó họ thêm metric stale_event_rejected_total, log cả incoming/current version và giữ event bị từ chối cho reconciliation. Bỏ một record cũ mà không quan sát được cũng nguy hiểm như áp nó.
Chờ reorder buffer theo event time giúp giảm out-of-order, nhưng tăng latency và state. Version guard phản hồi ngay, nhưng cần domain owner phát sequence đúng. State machine bắt lỗi transition, nhưng migration và backward compatibility khó hơn. Reconciliation sửa sai sau cùng, nhưng người dùng có thể thấy trạng thái tạm thời.
Ben đóng incident với nguyên nhân “arrival order was treated as causal order”. Tom sửa một chữ: “was assumed.”
Đó là khác biệt Ben bắt đầu nhận ra: bug phân tán thường không nằm ở API. Nó nằm trong một giả định chưa từng được viết ra.
Và khi chuyển sang dashboard doanh thu theo phút, Fortex còn phải trả lời câu khó hơn: nên chờ event đến muộn bao lâu trước khi chốt số?
Apache Kafka 4.2.2 + confluent-kafka 2.15.1 · digest pinned · isolated Docker
Tải source Docker Lab (.zip)COMMIT 570ccb6b3b38b8121074bb56c07f3db638bf0961
Đọc kỹ README trước khi chạy. Các lệnh thực nghiệm chuẩn trong Docker Lab:
make setupmake seedmake reproducemake verifymake cleanupMôi trường Docker độc lập, giới hạn tài nguyên và tự động dọn dẹp sau khi chạy. Số liệu trong bài viết là kết quả lần chạy chính thức; mỗi lần chạy mới đều lưu lịch sử riêng.