- Consistency là bài toán khó nhất của distributed database.
- Hai cơ chế được dùng để kéo replica về cùng một trạng thái: read-repair khi đọc và hinted-handoff khi ghi.
- Đây là cách chúng hoạt động và lý do cả hai vẫn chỉ là best-effort.
TL;DR
Trong database phân tán kiểu leaderless (Dynamo, Cassandra, DynamoDB, Riak, ScyllaDB), các replica có thể lệch nhau bất kỳ lúc nào do mạng chập chờn hoặc node down. Hệ thống có hai điểm thời gian để tự sửa: lúc ghi bằng hinted-handoff và lúc đọc bằng read-repair. Cả hai đều best-effort — chỉ có anti-entropy repair (Merkle tree) mới thực sự đảm bảo hội tụ.
Bài toán: replica divergence
Khi bạn chạy một bảng với replication factor 3, mỗi row được copy lên 3 node. Nếu node B offline đúng lúc coordinator ghi một mutation, B sẽ bị miss write đó. Đến khi B sống lại, nó đang cầm dữ liệu cũ hơn A và C. Nếu client lỡ đọc đúng node B trước khi có gì sửa nó — client nhận dữ liệu stale.
Dynamo-style database không muốn chặn write chỉ vì một replica chết (đó là lý do chọn AP thay vì CP trong tam giác CAP). Vậy câu hỏi là: sửa replica lệch ở đâu, bao giờ, và bằng cách nào?
Hai điểm sửa lỗi
Có hai thời điểm tự nhiên để phát hiện và sửa divergence:
- Khi ghi — coordinator biết có replica không nhận được mutation, ghi chú lại để replay sau. Đây là hinted-handoff.
- Khi đọc — coordinator so sánh phản hồi từ nhiều replica, phát hiện mismatch, push bản mới nhất lên node stale. Đây là read-repair.
Cộng lại, hệ thống sửa trên cả write path và read path, không cần quét toàn bộ dataset liên tục.
Hinted-Handoff hoạt động thế nào
Khi coordinator gửi mutation tới 3 replica và một trong số đó không phản hồi:
- Coordinator ghi một hint lên ổ đĩa local. Hint chứa: ID replica đích, serialized mutation (blob), timestamp gốc, và Cassandra version.
- Write thành công với các replica còn sống (tuỳ consistency level).
- Coordinator gossip để biết khi nào replica kia sống lại.
- Khi replica trở lại, coordinator stream hints theo từng segment; replica replay local. Segment nào replay xong thì xoá.
Hint áp dụng idempotent — timestamp gốc được giữ, nên replay không thể ghi đè một mutation mới hơn đã tới bằng đường khác.
Số quan trọng: default max_hint_window = 3 giờ trong Cassandra. Node nào offline quá 3 giờ thì coordinator ngừng tích hint — vì lưu vô hạn là vô lý, và đằng nào cũng cần full repair.Read-Repair hoạt động thế nào
Khi client đọc với consistency level yêu cầu từ 2 replica trở lên:
- Coordinator gửi request tới replica, thường gồm 1 full-data read và N-1 digest reads (digest = hash nhanh của row).
- Nếu các digest khớp nhau → trả lời client ngay.
- Nếu mismatch → coordinator lấy full data từ tất cả replica, xác định bản mới nhất theo timestamp, và push update về các replica stale.
Read-repair có hai chế độ:
- Foreground (blocking): coordinator đợi các replica stale ack update rồi mới trả client → tăng latency nhưng đảm bảo lần đọc tiếp theo chắc chắn nhất quán.
- Background (non-blocking): trả client ngay, repair chạy async → latency đẹp, nhưng có race window ngắn.
So sánh ba cơ chế
| Cơ chế | Khi nào chạy | Path | Guarantee | Chi phí |
|---|---|---|---|---|
| Hinted-handoff | Write (replica down) | Write | Best-effort, ≤ 3h | Rẻ, bounded |
| Read-repair | Lúc đọc | Read | Best-effort, chỉ key được đọc | Thêm latency khi mismatch |
| Anti-entropy repair | Định kỳ / nodetool repair | Background | Guaranteed hội tụ | Đắt, quét full data |
Điểm chí mạng: read-repair chỉ sửa key được đọc. Cold data (dữ liệu không ai động tới) có thể thối âm thầm cho tới khi anti-entropy quét tới. Và hints chỉ giúp nếu node quay lại trong window — vượt quá thì coi như chưa có gì.
Điểm khác Cassandra vs DynamoDB
Hai database cùng họ Dynamo nhưng khác nhau ở cách tính quorum có bao gồm hint hay không:
- Cassandra — strict quorum: hinted writes không được tính vào write consistency (trừ
CL=ANY). Nếu đủ replica không sống thật, write fail. - DynamoDB — sloppy quorum: hinted writes có được tính. Đổi lại write availability 100%, nhưng read có thể stale trong khoảng thời gian hint chưa replay.
Đây là ví dụ điển hình của tradeoff CAP: cùng cơ chế, hai cách chọn, hai hành vi khác nhau.
Use cases thực tế
- Time-series / IoT telemetry — hinted-handoff cho phép ghi liên tục kể cả khi đang rolling restart cluster.
- Social feed, catalog đọc nhiều — read-repair opportunistic heal hot path mà không cần chạy repair thủ công.
- Multi-region deployments (DynamoDB Global Tables, Cassandra multi-DC) — hai cơ chế này cover các partition mạng tạm thời giữa các DC.
- Ops đơn giản hơn — node blip vài phút không cần rebuild; hint tự chạy lại.
Giới hạn & lưu ý
- Hint window 3h là cứng. Node down lâu hơn → phải
nodetool repairtay. - Hint có thể biến mất nếu chính coordinator crash trước khi replay (phantom consistency).
- Read-repair bỏ quên cold data — phải chạy anti-entropy định kỳ (tuần / tháng tuỳ cluster).
- Blocking read-repair = 2× round trip khi mismatch — tail latency xấu.
- Không có cơ chế nào trong ba cơ chế này thay thế được
nodetool repair/ incremental repair. Đừng tin mỗi hint + read-repair là xong.
Kết & định hướng
Hinted-handoff và read-repair không phải là magic — chúng là hai best-effort optimization giúp giảm thời gian replica ở trạng thái lệch, chứ không đảm bảo hội tụ. Cái thật sự đảm bảo là anti-entropy repair qua Merkle tree, chạy định kỳ. Trend hiện tại (Cassandra 4.x+, ScyllaDB) là incremental repair — chỉ đối chiếu phần dữ liệu chưa từng được repair — để giảm cost của full repair, kết hợp với Paxos-based lightweight transactions cho workload cần stronger-than-eventual.
Lần tới khi bạn thấy một write thành công trên cluster đang có node chập chờn — có thể đó là hint đang âm thầm gánh. Lần tới khi một query lạ nhiên đột nhiên về đúng — có thể read-repair vừa kịp sửa giúp bạn.
Nguồn: Apache Cassandra — Hints, Cassandra Dynamo architecture, Dynamo vs Cassandra, InfluxData — Anti-Entropy, @BenjDicken.