Inside Cassandra: ring, gossip, replicas, quorums; HBase
After this lesson you will trace a key from its hash to the nodes that hold its copies, follow a write and a read inside a Cassandra node, tell which consistency levels guarantee a fresh read, and say what HBase does differently.
The slide "Vs. SQL" (p. 459) prints four numbers, measured "On > 50 GB data". MySQL: writes 300 ms, reads 350 ms on average — about the same. Cassandra: writes 0.12 ms, reads 15 ms. Cassandra writes 125 times faster than it reads. What did it give up to make writing so cheap?
The second puzzle comes from the company that built Cassandra. In November 2010 Facebook chose HBase, not Cassandra, for its new Messages system: "We found Cassandra's eventual consistency model to be a difficult pattern to reconcile for our new Messages infrastructure" (Facebook Engineering). Yet HBase writes with the same machinery — a log, a table in memory, sorted files on disk. Where does the different consistency come from?
Keep both questions. Section 3 answers the first, section 5 the second.
Where a key lives: the hash ring
Take the paper of 12 Sep 2025: its farmers' sales, "large-scale, continuously increasing", go to Cassandra, to be queried "by market location or product name". Partition them by market location: every Milano sale lands in the partition 'Milano'. Eight machines hold them as equals: "NO MASTER", "NO SLAVE", "PEER TO PEER" (p. 440). So who answers p. 439, "How do you decide which server(s) a key-value resides on?"
Every node, alone, with the same arithmetic. The partitioner turns the key into a number, its token: "system will use MD5(key) to distribute data across nodes" (p. 457). Tokens lie on a circle; each node owns one token, and a key belongs to the first node clockwise from its own token. The replication factor N counts the copies, and the Simple Strategy places the other N − 1 by "Clockwise placement to the next node(s)" (p. 442). A client may send a request to any node, which becomes that request's coordinator (p. 443).
Why a circle, and not the hash modulo the number of nodes? With modulo 8, a ninth machine changes the remainder of almost every key: nearly all the data moves. On the ring a new node takes one arc from one neighbour. That is consistent hashing (background: the slides draw the ring without naming it).
Two partitioners
The "partitioner smack-down" (p. 457) sets the two side by side (Table 1). Keeping keys in order lets the order-preserving one scan a range of keys, at the price of the "'scrabble' distribution": real keys are as uneven as Scrabble letters, and the node owning the M's gets far more rows than the one owning the X's. The hash is the default (background): Cassandra gives up range scans over partition keys and keeps order inside a partition instead, with the clustering key of lesson 21.
| Random (hash) | Order preserving | |
|---|---|---|
| token | "system will use MD5(key) to distribute data across nodes" | "key distribution determined by token"; "can specify the token for this node to use" |
| row order | hash order | "lexicographical ordering" |
| range of keys | not scannable | "required for range queries": "scan over rows like cursor in index" |
| load | "even distribution of keys from one CF across ranges/nodes" | "'scrabble' distribution": hot spots |
Where the copies go
"data replication" (p. 456) needs a "configurable replication factor" and a "replica placement strategy". The slide strikes out the old, more telling names: rack unaware → Simple Strategy; rack aware → Old Network Topology Strategy (since removed, background); data center shard → Network Topology Strategy. The Simple Strategy serves a "Single datacenter" (p. 442) and ignores racks. The Network Topology Strategy serves "Multiple datacenters", "Supporting local queries": a replication factor per datacenter, and inside each one the walk skips nodes of a rack that already holds a copy (Fig. 1).
Simple Strategy, N = 4 (computed)
Network Topology Strategy, 2 + 2 (p. 444)
A Cassandra script starts by creating the keyspace, as in the exercise session (p. 1553):
-- three copies of every row
CREATE KEYSPACE population
WITH replication = {
'class': 'SimpleStrategy',
'replication_factor': 3};
Per datacenter, in the syntax of the Cassandra documentation (not on the slides), for Fig. 1:
-- two copies in each datacenter
CREATE KEYSPACE population
WITH replication = {
'class':
'NetworkTopologyStrategy',
'DC1': 2, 'DC2': 2};
The hash picks the first node clockwise; the strategy picks the other N − 1: the next nodes, or the next racks of each datacenter.
Nobody in charge: gossip and failure detection
Node B3 reboots at night. With no master, nobody keeps the list of who is alive: each node keeps its own and corrects it by talking to the others. Page 440 draws a rumour — "I'm online!" — travelling from node to node as "gossip".
The "Gossip protocol" (p. 441) runs in rounds. "Each node picks its discussants (up to 3)", however large the cluster, and each conversation is a three-message handshake (Fig. 2). Afterwards both know the union of what they knew, so views "converge": the slide's anti-entropy, entropy being the disorder of nodes that disagree. Few partners per round mean "Always a constant amount of network traffic (except for gossip storms)", when many nodes change state at once.
"Up to 3" discussants and "three messages for each round" are two different threes: partners per round, messages per conversation. And gossip carries node state — heartbeats, tokens, versions — never rows: writes go from the coordinator to the replicas (p. 445).
The membership list
"Cluster Membership" (p. 451): each node keeps one row per node — Address, Heartbeat Counter, Time (local) — and raises only its own counter. "Nodes periodically gossip their membership list"; "On receipt, the local membership list is updated": per address the larger counter wins, stamped with the receiver's own clock, since the clocks are "asynchronous". Fig. 3 replays the slide.
Before: node 1's list arrives
After: node 2 at time 70
From silence to suspicion
A silent node may be dead or only slow, and a fixed timeout errs one way or the other. The accrual failure detector of p. 452 "outputs a value (PHI) representing suspicion", "Apps set an appropriate threshold", and "PHI = 5 => 10-15 sec detection time". PHI, φ, grows with the silence, measured against the "Inter-arrival times for gossip messages":
Let \(\Delta t = t_{now} - t_{last}\) be the silence and F the distribution of past gaps. A live node stays silent this long with the tail probability
\[ P_{\text{later}}(\Delta t) = 1 - F(\Delta t) \]φ = 1 means a 10 % chance that a live node is this late; 2 means 1 %, 5 means 0.001 %:
\[ \varphi = -\log_{10} P_{\text{later}}(\Delta t) \]Background: Cassandra takes the gaps as exponential with mean μ, so \(P_{\text{later}} = e^{-\Delta t / \mu}\) and
\[ \varphi = \frac{\Delta t}{\mu \ln 10} \]With gossip about once a second (μ ≈ 1 s, background), φ = 5 after 5 ln 10 ≈ 11.5 s — inside the slide's 10–15 s. A slower network has a larger μ and a later alarm: the slide's "sensitive to actual inter-arrival time variations".
A write is an append; a read is a merge
A sale arrives for 'Milano'. "Writes" (p. 445): the client "sends write to one front-end node in Cassandra cluster (Coordinator)", "Which (via Partitioning function) sends it to all replica nodes responsible for key".
"Always writable: Hinted Handoff": "If any replica is down, the coordinator writes to all other replicas, and keeps the write until down replica comes back up" — the kept write is a hint — and "When all replicas are down, the Coordinator (front end) buffers writes (for up to an hour)". The slide adds "Atomicity for a given key (i.e., within ColumnFamily)": one write to one partition is applied whole, with no multi-key transactions. And "One ring per datacenter": the coordinator "can also send write to one replica per remote datacenter".
At the replica: commit log, memtable, SSTable
Writes "Need to be lock-free and fast (no reads or disk seeks)". At the replica (p. 446): "1. log it in disk commit log" — an append, so no seek, and the write survives a crash; "2. Make changes to appropriate memtables" — a memtable is an "In-memory representation of multiple key-value pairs", sorted by key. Then the replica acknowledges (Fig. 4).
"Later, when memtable is full or old, flush to disk": once, in key order, as an SSTable ("Sorted String Table – list of key value pairs, sorted by key"), with an index of "(key, position in data sstable) pairs" "And a Bloom filter". SSTables are never modified: newer versions of 'Milano' land in newer files, and compaction merges SSTables, keeping each key's newest version ("Merge key updates, etc.").
The Bloom filter: a cheap "no"
To skip the SSTables that cannot hold a key, each has a Bloom filter (p. 447), a "Compact way of representing a set of items": a bit array and k hash functions. "On insert, set all hashed bits." "On check-if-present, return true if all hashed bits set." One 0 proves absence; all 1s prove nothing — other keys may have set them. Hence "Some probability of false positives" but "Never false negatives".
The slide's example — "k=4 hash functions", "100 items", "3200 bits", "FP rate = 0.02%" — checks out with the standard formula (background): a bit stays 0 with probability about \(e^{-kn/m}\), so
- \(m\)
- bits in the array (3200)
- \(n\)
- keys inserted (100)
- \(k\)
- hash functions (4)
32 bits per key, whatever the key: a "no" costs k bit tests in memory, not a disk read.
Deletes write a tombstone
Immutable files cannot erase a row. "Delete: don't delete item right away" — "add a tombstone to the log"; "Compaction will remove tombstone and delete item" (p. 448). A tombstone is a timestamped delete marker written like any write; it hides older values from reads (Fig. 5). Until compaction a delete adds data (background: a tombstone outlives a grace period, ten days by default, so that replicas that were down hear of it).
Before: three SSTables
After: one SSTable
Reads merge
A read merges 'Milano' from the memtable and the SSTables whose filter says "maybe", keeping the newest timestamp: reads "May be slower than writes" (p. 446).
Page 455 adds the "Staged Event-Driven Architecture": "A general-purpose framework for high concurrency & load conditioning" built of "stages separated by queues". The slide leaves the link implicit (background): in a Cassandra node writes, reads, gossip and compaction are such stages, so overload shows as a growing queue, and a slow compaction does not stop gossip.
Back to the first puzzle
So the 0.12 ms against 15 ms of p. 459: a write is one log append and one memory insert, with no read, seek or lock; a read merges several files, often on several replicas. Cassandra gave up cheap reads and in-place updates, and pays later instead: flushes, compactions, tombstones. The slide names no hardware or workload: read a ratio, not a benchmark.
How many copies must answer: quorums
Three copies of 'Milano': how many must answer a write, and how many a read? The client chooses (p. 449): "R = read replica count, W = write replica count", "N = total number of replicas for this key".
Reads "Wait for R replicas (R specified by clients)" and check the "remaining N-R replicas" in the background. The coordinator "can contact closest replica (e.g., in same rack)", "also fetches from multiple replicas", and starts a read repair "if any two values are different" (p. 448). Writes "come in two flavors": "Block until quorum is reached", or "Async: Write to any node". A quorum is a majority of the N copies.
- \(N\)
- replication factor: copies of the key
- \(\lfloor \cdot \rfloor\)
- round down
"For N = 3, the slide's 'Quorum Q = N/2 + 1' (p. 449) is 2.5 replicas." No: the division rounds down, as in the Cassandra documentation — 2 of 3, 3 of 4, 3 of 5 — and so does the "(N /2) + 1" of p. 453. Write the floor.
Why W + R > N is enough
The slide: "If W+R > N and W > N/2, you have consistency", and "Allowed (W=1, R=N) or (W=N, R=1) or (W=Q, R=Q)". The first half does the work:
The write was acknowledged by a set A of W replicas; a later read hears from a set B of R replicas, out of the same N:
\[ |A \cup B| \le N \]So the two sets share at least
\[ |A \cap B| = W + R - |A \cup B| \] \[ |A \cap B| \ge W + R - N \]If W + R > N, a replica answering the read holds the write, and the coordinator returns the most recent timestamp: "Return most recent TS" (p. 453).
The slide's corners pass: 1 + N, N + 1 and 2⌊N/2⌋ + 2 all exceed N.
The corners pay differently: (1, N) has fast writes, but a read needs every replica up; (N, 1) has fast reads, but one replica down stops the writes; (Q, Q) balances both and survives N − Q failures — one of three, two of five.
"W > N/2 is required too." Not by the slide's own list: (W=1, R=N) is "Allowed", and 1 is not more than N/2. W > N/2 keeps two writes from being accepted by disjoint sets; Cassandra settles concurrent writes by timestamp, and its documentation states the rule as W + R > N alone.
Consistency levels
"In reality, a client can choose one of these levels for a read/write operation" (p. 450; Table 2). The older tables of p. 453 use old names: ZERO ("Good luck with that") is gone, and "DCQ for RackAware" is today's LOCAL_QUORUM. In cqlsh the level is set for the statements that follow (documentation syntax, not on the slides):
-- next statements: wait for a majority
CONSISTENCY QUORUM
| Level | Waits for (p. 450) | Write (p. 453) | Read (p. 453) |
|---|---|---|---|
ANY | "any node (may not be replica)" | "1 replica (hints count)" | "Try ONE instead" |
ONE | "at least one replica" | "1 replica. read repair in bkgnd" | "1 replica" |
QUORUM | "quorum across all replicas in all datacenters" | "(N /2) + 1" | "Return most recent TS after (N /2) + 1 report" |
LOCAL_QUORUM | "in coordinator's DC" | "DCQ for RackAware" in the old tables | |
EACH_QUORUM | "quorum in every DC" | — | |
ALL | "all replicas all DCs" | "N = replication factor" | "N = replication factor" |
"The level ANY works for reads too." Page 450 lists it for "a read/write operation", but a hint cannot be read: p. 453 answers a read at ANY with "Try ONE instead".
With three copies in each of two datacenters (computed): QUORUM waits for 4 of the 6, wherever they are; LOCAL_QUORUM for 2 in the coordinator's datacenter, with no trip to the other — what "Supporting local queries" buys; EACH_QUORUM for 2 in each, the strictest and slowest.
The trade, and when it pays
"Cassandra Summary" (p. 460): BASE, "Basically Available Soft-state Eventual Consistency"; Cassandra "Prefers Availability over Consistency", HBase "Prefers (strong) Consistency over Availability" — the CAP corners of lesson 5. Yet the choice is per operation: ONE and ANY keep accepting writes while replicas are down, QUORUM and ALL refuse rather than answer stale — "Eventual consistency, tunable per query" (p. 1550). Table 3 says when it pays.
| It pays when… | …and the team can take |
|---|---|
| "you need really fast writes" | "your programmers can deal": documentation, complexity, consistency model, change, visibility tools |
| "you need durability" | "your operations can deal": hardware considerations, can move data, JMX monitoring |
| "you have lots of data": > GBs, >= three servers | |
| "your app is evolving": startup mode, fluid data structure | |
| "loose domain data": "points of interest" |
A read is sure to see the latest acknowledged write when W + R > N; a quorum is ⌊N/2⌋ + 1, chosen per operation.
HBase: the same engine, one owner per row
"HBase" (p. 463) brings Google's Bigtable design into Hadoop: "Major Apache project today, part of Hadoop"; "Facebook uses HBase internally". Its API (p. 464): Get(row), Put(row), Scan(row range, filter) ("range queries") and MultiPut(rows). The scan is the first difference: rows stay sorted by row key, as with the order-preserving partitioner, so a range of rows is one sequential read. (Data model: lesson 32.)
The architecture of p. 465 (Fig. 6) has two floors. Upstairs, HBase: a client asks ZooKeeper which HRegionServer serves its rows, then talks to it directly; the HMaster assigns regions and watches the servers. Downstairs, Hadoop: every byte lives in HDFS DataNodes, which keep the copies.
The "HBase Storage hierarchy" (p. 466), outside in: a table splits into regions, ranges of row keys; a region has "One Store per ColumnFamily (subset of columns with similar query patterns)" — columns read together share a family, stored and read as one Store; a Store has a "Memstore" ("in-memory updates to Store; flushed to disk when full") and "StoreFiles … where the data lives", in blocks. A StoreFile is an HFile, the "SSTable from Google's BigTable": data blocks, indexes and a trailer, every key-value record carrying its whole key (p. 467, Table 4).
| Field | Census example |
|---|---|
| Key Length, Value Length | lengths of the two parts |
| Row Length, Row | SSN:000-00-0000 (15 characters) |
| Column Family Length, Column Family | Demographic (11 characters) |
| Column Qualifier | Ethnicity |
| Time Stamp, Key Type | version of the cell; a value or a delete marker (background) |
| Value | the stored bytes |
Write-ahead log and replay
"Strong Consistency: HBase Write-Ahead Log" (p. 468): a client's put(), delete() and incr() go "to HLog before writing to MemStore", so the server "Can recover from failure". The HLog is HBase's commit log, forced to disk by a Log Flusher (sync()) and cut into files by a Log Roller (rollWriter()). "Log Replay" (p. 469): "After recovery from failure, or upon bootup", "Replay any stale logs (use timestamps to find out where the database is w.r.t. the logs)" — "add edits to the MemStore". One HLog per HRegionServer, not per region, "Avoids many concurrent writes, which on the local file system may involve many disk seeks".
Across datacenters
"Cross-data center replication" (p. 470) reuses the log: each region server of the master cluster ships its HLog edits, in batches, to slave clusters ("Synchronous Call"). ZooKeeper, "actually a file system for control information", keeps /hbase/replication/state, /hbase/replication/peers/<peer cluster number> and /hbase/replication/rs/<hlog>: per region server, the HLogs to ship and the offset reached.
"'Synchronous Call' means a client's write waits for the other datacenter." No: each shipping call is synchronous, but replication runs behind the client — a write is acknowledged once it is in the local HLog and MemStore (background: the HBase Reference Guide calls the replication asynchronous).
Back to the second puzzle
| Cassandra | HBase | |
|---|---|---|
| who serves a row | N equal replicas, no master | one HRegionServer per region |
| finding it | the hash ring, known to all by gossip | ZooKeeper; HMaster assigns regions |
| log, memory, disk | commit log, memtable, SSTable | HLog, MemStore, HFile |
| copies | N replicas on N nodes | HDFS copies of the files |
| row order | hash order; order inside a partition | sorted by row key: Scan(row range) |
| p. 460 | "Prefers Availability over Consistency" | "Prefers (strong) Consistency over Availability" |
Same engine, different owner. Cassandra lets any of N equal replicas take a write, so writes never wait for one machine, and replicas may disagree for a while. HBase gives each row one server, so a read sees every acknowledged write of the row, with no quorum arithmetic; but while a crashed server's regions are reassigned and its HLog replayed, those rows cannot be served. Facebook chose that simpler consistency for Messages.
The other columnar: storing each column apart
The deck closes with "Other Columnar DBs" (p. 471) — another meaning of "columnar". Cassandra and HBase keep a row's columns together (HBase within each column family); MonetDB and LucidDB store each column apart, the column storage of lesson 20, for queries that read a few columns of many rows.
"MonetDB" (p. 472) runs an SQL front-end (relational model), an ODMG front-end (object-oriented model), an OLAP tool and a data-mining tool on one "Monet back-end" through the MIL Language, with "full vertical fragmentation": on p. 473 (Fig. 7) Order(id, day, discount) becomes order_id, order_day and order_discount, each pairing an object id, the oid, with one value.
"LucidDB" (pp. 474–476) keeps "column store tables" in one file, db.dat. A table "consists of set of clusters", by default one per column ("Each column maps to single cluster"), and a cluster page "stores the values for a specific set of rowIDs". A B-tree per cluster "maps rid values to pageIds" by each page's first rid: page 49 starts at rid 984 and page 50 at 1248, so page 49 holds rids 984–1247. Inside a page (p. 477) LucidDB stores "just the unique column values", each with "a bit-encoded vector" (Table 6).
| Distinct value | rid 0 | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|---|
| 0.175 | 1 | 0 | 1 | 0 | 0 | 0 |
| 0.065 | 0 | 1 | 0 | 0 | 0 | 1 |
| 0.000 | 0 | 0 | 0 | 1 | 1 | 0 |
Hence the "Conclusion" (p. 478): "Column architecture doesn't read unnecessary columns"; "Avoids decompression costs and performs operations faster" — discount = 0.000 is answered by the vector 0 0 0 1 1 0, decompressing nothing; "Use of compression schemes allow us to lower our disk space requirements".
Exam-style questions
Five theory questions in the forms of the theory part — open answers of a few sentences, and closed ones where a wrong tick may cost points — then two exercises: a quorum computation and a deployment question on the ER scenario of a past paper.
Describe step by step what a Cassandra replica node does when it receives a write, and explain why reads can be slower than writes.
Show model answer
The replica first appends the write to the commit log on disk, a sequential append that makes it durable, and then applies it to the memtable, an in-memory structure of key-value pairs sorted by key; then it can acknowledge. No read, no disk seek and no lock are needed. When the memtable is full or old, it is flushed to disk as an SSTable (Sorted String Table): an immutable file of key-value pairs sorted by key, with an index file of (key, position) pairs and a Bloom filter. Because SSTables are never modified, new versions and deletes (tombstones) accumulate in newer SSTables, and compaction periodically merges them, keeping the newest version of each key and removing deleted items. A read, instead, must look in the memtable and possibly in several SSTables — skipping those whose Bloom filter says the key is absent — and merge the versions by timestamp; the coordinator may also contact several replicas and start a read repair. This is why reads may be slower than writes.
A complete answer gives the order (commit log, then memtable), the words SSTable, flush, compaction and Bloom filter, and the reason for the asymmetry. The slide of p. 446 says reads "touch log and multiple SSTables": write memtable, and mention the log only for restarts.
A keyspace has replication factor N = 3. Which write/read consistency-level pairs guarantee that a read returns the most recent acknowledged write? Select all that apply.
Show model answer
QUORUM/QUORUM, ALL/ONE and ONE/ALL. The condition is W + R > N (here 2 + 2, 3 + 1 and 1 + 3, all greater than 3): it forces the replicas that acknowledged the write and the replicas that answer the read to overlap in at least one node, and the coordinator returns the value with the most recent timestamp. ONE/ONE and ONE/QUORUM fail the inequality; ANY guarantees no replica at all, because a hint stored on the coordinator counts as a successful write.
Three correct options out of six. ANY/ALL is the trap: it looks like the "read everything" corner, but W counts only real replicas. Wrong ticks may be penalised, so tick only what the inequality proves.
True or false: a Bloom filter may report that a key is absent from an SSTable although the key is stored in it.
Show model answer
False. When a key is inserted, all its k hashed bits are set, and bits are never reset, so a stored key always finds its bits set and the filter answers "maybe present". The filter can only err the other way: an absent key may find all its bits set by other keys (a false positive). The rate is low; the lecture's example has k = 4 hash functions, 100 items and 3200 bits, with a false-positive rate of 0.02 %.
A true/false question that tests the direction of the error. Write the reason, not just the word: "bits are never cleared".
Explain how the nodes of a Cassandra cluster learn which nodes are alive without a master. Describe the membership list and how a node decides that another node is down.
Show model answer
Cassandra is peer to peer: no master knows the membership, so each node keeps its own membership list with, for every node, its address, the latest heartbeat counter heard and the local time at which that counter was received. Each node increments its own counter and periodically gossips its list to a few peers (up to three per round), with a three-message exchange (GossipDigestSyn, GossipDigestAck, GossipDigestAck2). On receipt, for every address the node keeps the larger counter and stamps it with its own local time, because the clocks are not synchronised: node 2 at local time 70 receives 10120 and 10098 for nodes 1 and 3, larger than its 10118 and 10090, and stores (10120, 70) and (10098, 70), while it keeps its own entry and the equal one. The traffic per node stays constant and the views converge (anti-entropy). A node whose counter stops increasing becomes suspect: the accrual failure detector computes a suspicion value PHI, minus the base-10 logarithm of the probability that a heartbeat arrives later than the silence observed so far, from the past inter-arrival times; the application sets the threshold (on the slide, PHI = 5 gives a detection time of 10–15 seconds).
A complete answer has three parts: no master and gossip with few peers; the merge rule with local times; suspicion as a value with a threshold rather than a fixed timeout. The numbers of p. 451 make the merge rule concrete.
Cassandra "prefers availability over consistency", while HBase "prefers (strong) consistency over availability". Explain where this difference comes from in the two architectures.
Show model answer
In Cassandra every row has N equal replicas on a masterless ring. Any node can coordinate a write and sends it to all replicas; with levels such as ONE or ANY, and with hinted handoff, a write is accepted even while replicas are down, so the replicas may hold different versions for a while; they are reconciled later by timestamps, read repair and compaction (eventual consistency, BASE). In HBase a table is split into regions, and each region is served by exactly one HRegionServer at a time, assigned by the HMaster through ZooKeeper. Every write of a row goes through that single server, which appends it to its write-ahead log (HLog) before applying it to the MemStore, so every read of the row sees the latest write. The copies of the data are kept by HDFS at the file level, not by several servers answering for the same row. When a region server fails, its regions are unavailable until the HMaster reassigns them and the HLog is replayed into the new MemStores: HBase gives up availability during recovery to keep strong consistency.
The core is "N equal replicas" against "one owner per region". The write-ahead log alone gives durability; consistency comes from the single owner. Mention BASE for Cassandra, and the unavailability during recovery for HBase.
A keyspace uses the Simple Strategy with replication factor 4. Writes are performed at consistency level QUORUM. What is the smallest number of replicas a read must wait for, so that it is guaranteed to return the latest acknowledged write?
Show model answer
QUORUM with N = 4 is ⌊4/2⌋ + 1 = 3 replicas, so W = 3. A fresh read needs W + R > N, that is R > 4 − 3 = 1, so R = 2 replicas. Of the lecture's levels, ONE is not enough and QUORUM (3) is more than needed.
The two steps are the quorum size and the inequality. With N = 4 the slide's N/2 + 1 gives 3 with or without rounding; with an odd N only the rounded-down form is a whole number, so always write the floor.
ER scenario of the exam of 12 Sep 2025 (the exercise itself is exam-style, not from the paper): a data structure for tracking farmers and their sales at local markets. Market (Name, Location, Opening Time, Closing Time, Size) and Product (Product ID, Name, Quantity, Expiration Date) are linked by the relationship Sell, with cardinality (1, N) on both sides.
The sales are stored in Cassandra, partitioned by market location, on one datacenter of six nodes n1–n6 with tokens 10, 25, 40, 55, 70 and 85; the token of the partition 'Milano' is 62. Write the CQL statement that creates the keyspace markets so that every row is stored three times. Say which nodes hold the copies of 'Milano', and choose consistency levels for writes and reads so that a read always returns the latest acknowledged sale while one replica may be down.
Show model answer
CREATE KEYSPACE markets
WITH replication = {
'class': 'SimpleStrategy',
'replication_factor': 3};
With the Simple Strategy the first copy goes to the first node clockwise from the token 62, that is n5 (token 70), and the other two to the next nodes clockwise, n6 (85) and n1 (10, after wrapping around the ring). Writes and reads at QUORUM wait for ⌊3/2⌋ + 1 = 2 replicas each: 2 + 2 > 3, so every read overlaps the last acknowledged write, and either operation still succeeds with one replica down. (ONE/ALL or ALL/ONE would also be consistent, but a single replica down would block the reads or the writes.)
The keyspace line opens every Cassandra script of the exercise session (p. 1553); here it is joined to the placement and quorum rules of this lesson. The wrap-around from n6 to n1 is where answers usually slip.
Check yourself
In the ring widget, five nodes hold the keys with N = 1. B3 joins with the token 68, between A2 (56) and B2 (81). Which keys change node?
Which statements about gossip in Cassandra are true? Select all that apply.
Gossip spreads state about nodes, cheaply and repeatedly; the decisions — dead or alive — are taken by each node for itself.
Node 3 is at local time 42. Its list holds the row 1: (counter 500, time 30). A gossip message from node 4 carries 1: (498, 41). What does node 3 store for node 1 afterwards?
You delete the partition 'Lodi' and look at a replica's disk right away. What do you find?
N = 3. A client writes a new price at ONE and, a millisecond later, another client reads it at ONE. What can happen?
Which statements about HBase are correct? Select all that apply.
In LucidDB's rid-to-pageId B-tree (p. 476), page 49 starts at rid 984 and page 50 at rid 1248. On which page is rid 1247?
Key take-aways
- A partitioner turns a key into a token — MD5 on the slide (p. 457) — and the first node clockwise on the ring owns it. The replication factor N sets the number of copies; the Simple Strategy puts them on the next nodes clockwise, the Network Topology Strategy per datacenter on different racks (pp. 442–444, 456). The order-preserving partitioner allows key ranges but gives the "'scrabble' distribution".
- With no master, nodes gossip with up to three peers per round in a three-message handshake (p. 441). Membership lists merge by the larger heartbeat counter, stamped with the receiver's own time (p. 451); the accrual detector outputs a suspicion φ = −log₁₀ of the chance of a later heartbeat, against a threshold the application sets (p. 452).
- The coordinator sends a write to all replicas and keeps hints for those that are down (p. 445). A replica appends to the commit log and updates the memtable; full memtables are flushed to immutable, sorted SSTables with an index and a Bloom filter; compaction merges them; a delete writes a tombstone (pp. 446–448, 454).
- A read merges the memtable and the SSTables its Bloom filters do not rule out — never the commit log — so reads are slower than writes (pp. 446, 459). A Bloom filter has false positives but never false negatives (p. 447).
- Q = ⌊N/2⌋ + 1, and a read is sure to see the latest acknowledged write when W + R > N (p. 449). The levels are
ANY(writes only),ONE,QUORUM,LOCAL_QUORUM,EACH_QUORUMandALL(pp. 450, 453); read repair is done by the coordinator. - HBase splits tables into regions, each served by one HRegionServer, with one Store per column family (MemStore plus HFiles) and one HLog per server written before the MemStore and replayed after a crash; HDFS replicates the files (pp. 463–470). One owner per region gives strong consistency; Cassandra's N equal replicas give availability (p. 460).
- MonetDB and LucidDB are column stores in the other sense: MonetDB splits every column into its own (oid, value) table, LucidDB stores columns in clusters of pages with distinct values and bit vectors (pp. 471–478).
What to watch and read
-
Distributed Systems 5.2: Quorums
Martin Kleppmann, University of Cambridge
Read and write quorums with three replicas, and why r + w > n makes a read meet the latest write — the overlap of the quorum widget, drawn on a board.
-
Bloom Filters
Jason Davies
A 50-bit filter with three hash functions: add words, test words, and watch a false positive appear — a second playground next to the one in section 3.
-
Compaction visualizer
Tzach Livyatan (ScyllaDB)
Client, commit log, memtable, flush, SSTables and compaction, animated with live space and read amplification. Built for ScyllaDB, Cassandra's twin; its leveled, time-window and size-tiered strategies are Cassandra's too, the incremental one is ScyllaDB's own.
-
Dynamo
Apache Cassandra documentation
The official version of sections 1, 2 and 4: the token ring and virtual nodes, both strategies, gossip, the Phi accrual detector, hinted handoff, the consistency levels and the rule W + R > RF.
-
The Underlying Technology of Messages
Facebook Engineering, 2010
The source of the second puzzle: why the company that built Cassandra chose HBase, with "a simpler consistency model", for Messages.