Support: “User bảo số dư / đơn / dashboard sai.” API ghi vẫn 200. On-call mở Kafka lag, bấm replay, restart consumer — đôi khi đúng, đôi khi chữa nhầm bệnh.
Trên read side (projection, search, cache, replica đọc), “data sai” không phải một lỗi. Ít nhất ba kiểu hỏng + một trạng thái chưa chắc. Phân loại xong mới chọn recovery (playbook).
Demo (cùng event, bốn hậu quả view): stale · missing · wrong · uncertain.
Bốn ô — học thuộc trước khi mở tool
Stale — view từng đúng theo event đã apply; giờ lạc hậu (chưa bắt kịp)
Missing — event/record lẽ ra phải có trong view — không có (gap)
Wrong — view có dữ liệu nhưng sai nghĩa (order, double, map bug)
Uncertain — chưa biết effect đã commit hay chưa (timeout, in-flight)
User đều có thể nói “sai”. Bốn hướng sửa khác nhau.
Stale — cũ nhưng từng đúng
Consumer lag, cache TTL, read replica delay, batch index chậm. Pipeline write đã xong; read model chưa (hoặc cache còn bản cũ).
- Triệu chứng: “Vừa bấm xong, F5 vẫn số cũ”; lag metric tăng; sau vài giây/phút tự đúng.
- Không phải: số vĩnh viễn sai, hay mất hẳn bản ghi.
- Thường làm: scale/optimize consumer; SLA stale (“trong 30s”); soft refresh — không ignore message, không resend shared lung tung.
- Trade-off: stale trên read thường chấp nhận hơn wrong — business write vẫn chạy; user thấy trễ.
Viết tắt: stale = đúng trong quá khứ gần, chưa phải hiện tại.
Missing — thiếu
Event bị skip/ignore, filter bug, publish mất (dual-write), DLQ rồi không backfill, consumer commit offset dù chưa apply…
- Triệu chứng: “Đơn có trong DB ghi, list search không có”; gap offset; mãi không tự lành.
- Nguy hiểm: user tin “chưa tồn tại” → tạo trùng, support “mất dữ liệu”.
- Thường làm: tìm gap (id/sequence), backfill/replay có quy trình; sửa publisher/outbox; cấm ignore mặc định trên projection bắt buộc đủ.
- DLQ trên read: unblock lane được nhưng tạo missing nếu không replay — phải gắn runbook.
Viết tắt: missing = lỗ hổng trong lịch sử view.
Wrong — có mà sai nghĩa
Apply hai lần không idempotent, out-of-order (update trước create), map field sai, timezone, “last write wins” nhầm key…
- Triệu chứng: số lệch, trạng thái không thể (cancelled nhưng vẫn active), không tự hết khi lag = 0.
- Nguy hiểm nhất với trust: user thấy có data và tin — decision dựa trên sai.
- Thường làm: dừng hoặc giảm ghi projection; fix handler + idempotency; rebuild từ log/source; kiểm order budget per-key.
Viết tắt: wrong = view không khớp sự thật đã xảy ra, kể cả khi “đủ event”.
Uncertain — chưa chắc
Timeout sau khi server có thể đã commit; message at-least-once đang bay; UI optimistic chưa confirm; “click hai lần, không biết trừ mấy lần”.
- Triệu chứng: client 504, DB có/không tùy lần; user hỏi “tiền đã trừ chưa?”.
- Sai lầm phổ biến: retry mù không key → biến uncertain thành double effect (wrong phía write).
- Thường làm: idempotent write/read theo key; tra cứu trạng thái; đừng spam; xem exactly-once thực dụng và retry cái gì.
Uncertain không phải “view hỏng” thuần — là bạn chưa có quan sát đủ. Xử lý như wrong (đập tay retry) hay missing (tạo lại) đều có thể phá.
Cùng một màn hình đỏ — bốn hướng
Hỏi nhanh:
1) Tự lành sau vài phút khi lag giảm? → nghi stale
2) Bản ghi/event không bao giờ xuất hiện? → nghi missing
3) Có data nhưng logic không thể / lệch? → nghi wrong
4) Vừa timeout / double-click / “không biết đã”? → uncertain
Đừng: “reindex tất cả” làm bước 0 cho mọi ticket.
Gắn recovery (read vs write)
Stale → throughput, SLA, cache policy (hiếm DLQ)
Missing → gap detect + backfill; cấm ignore mặc định
Wrong → fix + rebuild; idempotent; order per-key
Uncertain → quan sát / idempotent API; không đoán bằng retry mù
Write side “sai”:
hay là double / mất side-effect → idempotency, outbox, lease
(taxonomy trên vẫn giúp nói chuyện với read team)
Matrix strategy theo purpose: async recovery playbook. Unblock lane (unblock) trên read mà skip message = đánh đổi missing — nói to trade-off đó.
Checklist 1 phút khi user kêu “sai”
- Write path 200 và row nguồn đúng chưa? (tách write vs read)
- Lag / cache age / replica delay? → stale
- So nguồn vs view theo id: thiếu hẳn hay lệch field?
- Handler có idempotent / dedup key không?
- Có timeout/retry gần đó không? → uncertain trước khi replay
Đọc tiếp
- Write vs read vs shared — purpose quyết recovery.
- Consumer lag — stale đo được.
- Idempotency · Outbox — missing/wrong phía publish.
Một câu để nhớ
Đừng nói “data sai” — nói stale, thiếu, wrong, hay chưa chắc.
Cùng một ticket đỏ, bốn hướng chữa.
Phân loại là kỹ năng rẻ hơn reindex mù. Làm xong một lần, cả team nói chung được với support và với recovery playbook.