Case study — Cassandra ring & etcd Raft
Cassandra ghép consistent hashing + tunable quorum + gossip + Merkle tree ra sao; etcd dùng Raft cho config nhất quán mạnh thế nào. Vì sao một chọn AP, một chọn CP.
TL;DR: Cassandra và etcd đều là hệ phân tán nổi tiếng, nhưng chúng đặt cược khác nhau trên tam giác CAP. Cassandra ghép bốn thuật toán từ module này — consistent hashing phân mảnh dữ liệu lên ring, tunable quorum cho phép điều chỉnh mức nhất quán theo request, gossip lan truyền trạng thái membership, Merkle tree sửa chênh lệch giữa các replica — để đạt AP: luôn ghi được, chấp nhận eventual consistency. etcd chọn CP: Raft đảm bảo mọi đọc/ghi đều nhất quán mạnh, đổi lấy việc mất availability khi mất quorum. Bài học từ hai hệ thống: không có thiết kế "tốt hơn" — chỉ có thiết kế phù hợp với bài toán.
AP — consistent hashing + tunable quorum + gossip + Merkle tree
CP — Raft consensus cho config/service-discovery nhất quán mạnh
Phần A — Cassandra: bốn thuật toán ghép lại
1. Bài toán: dữ liệu người dùng ở quy mô Netflix
Apache Cassandra được thiết kế để lưu hàng trăm terabyte dữ liệu trải trên hàng trăm đến hàng nghìn node, không có single point of failure. Netflix dùng Cassandra để lưu viewing history, bookmark và user preferences — hệ thống phải luôn ghi được ngay cả khi một datacenter mất kết nối.
Yêu cầu:
- Phân mảnh — 100 TB dữ liệu phải phân bố đều trên hàng trăm node.
- Nhất quán — replica phải đồng bộ, phát hiện và sửa chênh lệch.
- Membership — mỗi node cần biết toàn bộ cluster đang có node nào, node nào đang chết.
- Fault tolerance — vẫn hoạt động khi một số node fail.
Cassandra giải quyết bốn yêu cầu này bằng bốn thuật toán từ module này:
2. Consistent hashing: token ring và virtual node
Cassandra dùng token ring — mỗi key được hash thành token trong không gian [0, 2^64), mỗi node sở hữu một khoảng token. Mỗi node vật lý được nhân bản thành nhiều virtual node (vnode) — mặc định 16 vnode mỗi node từ Cassandra 4.0 (bản cũ dùng 256) — để đảm bảo phân bố tải đều ngay cả khi node có phần cứng khác nhau.
-- Cassandra token assignment (đơn giản hóa)
-- Node A: owns tokens [100, 400), [700, 900) (2 vnode)
-- Node B: owns tokens [0, 100), [400, 700) (2 vnode)
-- Node C: owns tokens [900, 2^64) wrap [0, 0) (1 vnode)
function getCoordinator(partitionKey):
token <- murmur3Hash(partitionKey) -- Cassandra dùng Murmur3
return lookupRing(ring, token) -- binary search, O(log(N×vnode))
Khi thêm node mới vào Cassandra cluster, chỉ vnode của node mới cần streaming data từ node kế tiếp — phần còn lại không bị ảnh hưởng. Đây chính là tính chất K/N key di chuyển của consistent hashing từ bài 01 và mini-challenge vừa xong.
Replication factor (RF): Cassandra không chỉ lưu 1 bản — mặc định RF=3, nghĩa là mỗi key được lưu trên 3 node vật lý kế tiếp nhau trên ring. Khi node chết, 2 bản replica còn lại vẫn phục vụ được.
3. Tunable quorum: CAP tradeoff theo request
Cassandra cho phép client chỉ định consistency level cho từng request — đây là ứng dụng trực tiếp của quorum R+W>N từ bài 02:
| Consistency level | Ý nghĩa | R+W>N? | Tradeoff |
|---|---|---|---|
ONE | 1 replica xác nhận là đủ | Không đảm bảo | Nhanh nhất, có thể đọc stale |
QUORUM | Đa số replica xác nhận (N/2 + 1) | Có (với RF=3: R=W=2, R+W=4>3) | Cân bằng latency và consistency |
ALL | Tất cả replica xác nhận | Có | Nhất quán tuyệt đối, nhưng khi 1 node fail thì ghi/đọc fail |
LOCAL_QUORUM | Đa số replica trong 1 datacenter | Có trong DC | Dùng cho multi-datacenter |
Netflix thường dùng LOCAL_QUORUM cho đọc và LOCAL_QUORUM cho ghi — đảm bảo nhất quán trong mỗi datacenter nhưng không block khi một datacenter bị cô lập.
-- Write path với QUORUM (RF=3, cần 2 replica xác nhận)
coordinator <- getCoordinator(partitionKey)
replicas <- getReplicaNodes(ring, partitionKey, replicationFactor=3)
-- Ghi song song đến cả 3 replica
for each replica trong replicas:
sendWriteAsync(replica, mutation)
-- Chờ đủ quorum (2/3)
waitForAcks(count=2, timeout=10ms)
-- Nếu đủ 2 ack trong timeout: success
-- Nếu không: timeout error (replica thứ 3 vẫn được ghi async sau đó)
Đây chính là cơ chế AP của Cassandra: ghi thành công khi đủ quorum, ngay cả khi một số replica lag sau. Replica lag sẽ được sửa bởi anti-entropy.
Thử ngẫmNetflix dùng LOCAL_QUORUM cho cả đọc và ghi trong mỗi datacenter. Khi một datacenter bị cô lập hoàn toàn khỏi các DC khác, request trong DC đó còn thành công được không?
4. Gossip: membership và failure detection
Mỗi node Cassandra cần biết toàn bộ cluster — node nào đang sống, node nào đang chết, load của từng node là bao nhiêu. Với cluster 1 000 node, broadcast toàn bộ mỗi giây là không khả thi.
Cassandra dùng Gossip protocol (bài 05) — mỗi giây, mỗi node chọn ngẫu nhiên 1-3 node và trao đổi thông tin về trạng thái của mọi node mà cả hai biết. Sau O(log N) vòng, thông tin lan khắp cluster:
-- Cassandra gossip cycle (mỗi giây trên mỗi node)
peers <- selectRandom(cluster, count=3) -- chọn 3 node ngẫu nhiên
for each peer trong peers:
myState <- localNodeState() -- version, load, tokens, status
peerGossip <- exchange(peer, myState)
mergeState(peerGossip) -- cập nhật bảng trạng thái local
-- Sau ~log2(1000) ≈ 10 vòng gossip: mọi node biết trạng thái mọi node khác
Gossip cũng chạy Phi Accrual Failure Detector — thay vì "timeout = dead", tính xác suất node thực sự chết dựa trên lịch sử heartbeat. Điều này tránh false positive khi network tạm thời chậm.
5. Merkle tree: anti-entropy repair
Dù có quorum, replica vẫn có thể chênh lệch theo thời gian (network partition tạm thời, node crash giữa write). Cassandra chạy anti-entropy repair định kỳ bằng Merkle tree (bài 03):

Thứ đắt trong anti-entropy là dữ liệu truyền, không phải phép so. Ba bước đầu chỉ gửi hash — vài chục byte mỗi bước, bất kể token range có một triệu row hay mười triệu.
Thay vì so sánh từng row (hàng triệu row), Merkle tree cho phép xác định đúng đoạn token range bị chênh lệch trong O(log N) so sánh hash, rồi chỉ sync dữ liệu ở đoạn đó.
Phần B — etcd: Raft cho config nhất quán mạnh
6. Bài toán: source of truth của Kubernetes
etcd là key-value store phân tán được dùng làm backing store cho Kubernetes — lưu toàn bộ trạng thái cluster: pod, deployment, service, configmap, secret. Mỗi thay đổi trong Kubernetes (thêm pod, scale deployment, cập nhật config) phải được ghi vào etcd và được mọi control plane node nhìn thấy nhất quán.
Yêu cầu khác hoàn toàn với Cassandra:
- Strong consistency bắt buộc: hai controller đọc config cùng lúc phải thấy cùng một giá trị — không thể có split-brain.
- No stale read: nếu một node write
replicas=5, node khác đọc ngay sau phải thấy5, không phải3. - Chấp nhận giảm availability: nếu mất quorum (mất hơn N/2 node), etcd từ chối phục vụ thay vì trả về data có thể stale.
7. Raft trong etcd: leader là single source of truth
etcd triển khai Raft (bài 06) — mọi write phải đi qua leader, được nhân bản vào log của đa số node trước khi commit:
-- etcd write path (Raft)
client gửi PUT /key value đến bất kỳ etcd node
if node không phải leader: redirect về leader
-- Leader xử lý:
leader.log.append(entry={term, index, key, value})
-- Gửi AppendEntries đến tất cả follower song song
for each follower trong cluster:
sendAppendEntries(follower, entry)
-- Chờ đa số xác nhận (3/5 node = quorum)
waitForAcks(count=majority)
-- Khi đủ quorum: commit entry, apply vào state machine
leader.commitIndex <- entry.index
leader.applyToStateMachine(entry)
-- Báo success cho client
Mọi read cũng đi qua leader (trong chế độ linearizable mặc định) để đảm bảo không có stale read. Đây là điểm khác với Cassandra: Cassandra có thể đọc từ bất kỳ replica nào với ONE, etcd luôn đọc từ leader.

Mọi khác biệt ở hai cột đều truy được về một quyết định duy nhất. Chọn A thì không được có điểm nghẽn nào, nên không có leader, nên phải có gossip và Merkle để tự chữa về sau. Chọn C thì phải có một chỗ quyết định thứ tự, nên có leader, nên mất leader là ngừng ghi.
8. Vì sao etcd chọn CP, không AP như Cassandra
Đây là ứng dụng trực tiếp của CAP theorem:
| Cassandra | etcd | |
|---|---|---|
| Use case | User data, session, activity log — stale vài giây chấp nhận được | Kubernetes config, service discovery — split-brain có thể gây deploy loop hoặc double scheduling |
| CAP chọn | AP (availability + partition tolerance) | CP (consistency + partition tolerance) |
| Khi mất quorum | Vẫn đọc/ghi được với ONE, data có thể stale | Từ chối phục vụ cho đến khi phục hồi quorum |
| Độ trễ | Thấp (gossip, async) | Cao hơn một chút (Raft roundtrip đến đa số) |
| Consistency model | Tunable: eventual đến strong | Linearizable (strong) by default |
Kubernetes không thể chịu đựng stale config: nếu scheduler đọc stale và tin rằng pod chưa được schedule trong khi thực ra đã được schedule rồi, nó sẽ schedule lại — dẫn đến duplicate pod. etcd chọn CP để loại trừ khả năng này hoàn toàn.
Cassandra vs etcd: vì sao một chọn AP, một chọn CP

Cassandra AP phù hợp khi:
- Dữ liệu có thể được đọc lại (user preferences, activity log) — stale vài giây không gây hại.
- Scale cực lớn (hàng trăm node, petabyte) — Raft không scale tốt ở số node này.
- Cần geo-distribution với low latency writes ở mỗi datacenter.
etcd CP phù hợp khi:
- Config, locking, service discovery — bất kỳ inconsistency nào đều gây hành vi sai.
- Cluster nhỏ (thường 3-5 node) — Raft hoạt động tốt nhất ở quy mô này.
- Đổi được availability lấy correctness.
Liên hệ các bài khác
- Consistent hashing: token ring của Cassandra là ứng dụng trực tiếp — bài đó dạy cơ chế, case study này cho thấy cách Cassandra thêm RF và vnode vào.
- Quorum: tunable consistency level (ONE/QUORUM/ALL) của Cassandra là R+W>N ở các mức khác nhau. Bài đó chứng minh R+W>N đảm bảo overlap.
- Merkle tree: anti-entropy repair của Cassandra dùng Merkle tree để tìm đúng đoạn data bị chênh lệch mà không phải so toàn bộ.
- Gossip: Cassandra gossip protocol là ứng dụng chuẩn của epidemic broadcast — membership, failure detection, load balancing info.
- Raft: etcd là reference implementation Raft phổ biến nhất. Bài đó giải thích leader election và log replication — case study này cho thấy Kubernetes dùng nó ra sao.
Tự kiểm tra
- Q1Cassandra dùng consistent hashing với RF=3. Khi Node B fail, Cassandra vẫn phục vụ key của Node B như thế nào?
- Q2Tại sao etcd từ chối phục vụ khi mất quorum (mất hơn N/2 node), trong khi Cassandra vẫn tiếp tục với consistency level ONE?
- Q3Cassandra dùng Merkle tree để làm gì trong anti-entropy repair? Tại sao không so sánh từng row trực tiếp?
- Q4Trong Raft của etcd, tại sao mọi write phải đi qua leader? Điều gì xảy ra nếu cho phép follower nhận write trực tiếp?
- Q5Gossip protocol của Cassandra chỉ trao đổi với 1-3 node mỗi giây. Tại sao thông tin vẫn lan khắp cluster 1 000 node trong vài giây?
Bài tiếp theo: Module 4 — Tổng kết & cheat sheet
Bài này đáng gửi cho bạn học cùng?
Copy link đã gắn nguồn — dán group, chat, hoặc LinkedIn.
Bài này có giúp bạn hiểu bản chất không?
Hỏi đáp về bài này
Chưa có câu hỏi
Có gì chưa rõ trong bài? Đặt câu hỏi đầu tiên — câu trả lời từ cộng đồng giúp bạn (và người sau).
Đặt câu hỏi đầu tiên