Lesson 32 · Replication topologies · Module 3

Chain Replication

A quorum buys consistency by counting votes on every single operation. There is another shape that buys the same guarantee by arranging the replicas in a line — writes enter at one end, reads are answered at the other, and nobody votes on anything.

The win in this lesson: you will be able to explain why serving reads from the tail alone gives you linearizable reads with no coordination at read time, and say honestly what that costs against a quorum.

1. The problem: agreement is expensive per operation

Lesson 05 left you with a dial. Set R + W > N and every read contacts enough replicas to be sure it intersects the last write. That works, and the bill arrives on every operation: a read is a fan-out, and its latency is the slowest replica in the read set.

Chain replication asks a different question. Instead of proving at read time that this reader has seen enough replicas, arrange the replicas so that there exists one server whose state is, by construction, never ahead of anybody. Then a read of that one server needs no proof at all.

"Chain replication is a new approach to coordinating clusters of fail-stop storage servers. The approach is intended for supporting large-scale storage services that exhibit high throughput and availability without sacrificing strong consistency guarantees."

Chain Replication for Supporting High Throughput and Availability, van Renesse and Schneider, OSDI '04 · Abstract. De-hyphenated across a PDF line break: "with-out".

2. The arrangement

"In chain replication, the servers replicating a given object objID are linearly ordered to form a chain."

§3, Chain Replication

Three rules, and they are the whole protocol:

RuleThe paper's wordsConsequence
Updates enter at the head "Each update request is directed to the head of the chain." One server orders all writes
Queries are answered at the tail "Each query request is directed to the tail of the chain and processed there atomically using the replica of objID stored at the tail." One server orders all reads, against the oldest state in the chain
Every reply comes from the tail "The reply for every request is generated and sent by the tail." A write is acknowledged only once the whole chain holds it

Between head and tail, state changes move down a link that does not reorder or lose them:

"state changes are forwarded along a reliable FIFO link to the next element of the chain (where it is handled and forwarded), and so on until the request is handled by the tail."

§3, Chain Replication. De-hyphenated: "for-warded", "han-dled".
client W head middle tail client R the update enters at the head and walks down · every replica holds x = 3 update x = 7 forward x = 7 · reliable FIFO link window: the write is in flight and has been acknowledged to nobody query x x = 3 → the tail has not seen the update, so it is not committed forward x = 7 apply x = 7 this instant is the commit point ack update → the reply comes from the tail, never from the head after the acknowledgement, in real time query x x = 7 no read ever contacted the head or the middle · no quorum was counted anywhere
The concurrent read is the interesting one. It returns the old value, and that is not a staleness bug: at that instant the write had not returned to anybody, so an observer is free to order the read before it. The moment the tail applies the update is the moment the update becomes true — and it is the same server that decides what reads see.

3. Why this is strongly consistent

The paper's justification is one sentence, and it is worth memorising:

"Strong consistency thus follows because query requests and update requests are all processed serially at a single server (the tail)."

§3, Chain Replication. De-hyphenated: "re-quests".

Reads and writes are not merely each serialised — they are serialised against each other, at one place, in one queue. That is exactly the shape Lesson 23 asked for: a single global order that every client agrees on, with every operation appearing to take effect at one instant between its call and its return.

The structural fact underneath is the invariant that makes the tail special:

"Because updates are sent between elements of a chain over reliable FIFO links, the sequence of updates received by each server is a prefix of those received by its successor."

§3.1, Chain Replication · Update Propagation Invariant
update histories, latest at the right head u1 u2 u3 u4 u5 middle u1 u2 u3 u4 tail u1 u2 u3 acknowledged to clients in flight · no client has been told the tail's history is a prefix of every other replica's, so it never holds anything alone
Not a sequence diagram, because this is containment rather than messages over time. Histories shorten as you walk down the chain. The tail's is the shortest — which is precisely why reading it is safe.
The one idea to carry out of this lesson

A read needs no coordination when the server it reads holds only state that every other replica already has. The paper says it in a single line: "In chain replication, the tail is the one replica known to store all completed updates." (§6) A quorum discovers the committed value by intersecting sets at read time. A chain positions a server so that its local state already is the committed value.

And so the read costs what a read costs:

"Processing a query request involves only a single server, and that means query is a relatively cheap operation."

§3, Chain Replication

4. Failure: the master repairs the chain

Nothing above survives a crash on its own. A separate service — the master — detects failures, rewires each survivor's predecessor and successor, and "informs clients which server is the head and which is the tail of the chain." (§3.1)

4.1 Head failure — the cheap one

"This case is handled by the master removing H from the chain and making the successor to H the new head of the chain."

§3.1, Chain Replication · Failure of the Head

Requests the dead head had received but not yet forwarded are simply lost — and that is legal, because they were never acknowledged to anyone. Reads are untouched: "Head Failure. Query processing continues uninterrupted. Update processing is unavailable for 2 message delivery delays" (§4, de-hyphenated: "un-interrupted").

4.2 Tail failure — the expensive one

"This case is handled by removing tail T from the chain and making predecessor T− of T the new tail of the chain."

§3.1, Chain Replication · Failure of the Tail. De-hyphenated: "re-moving", "predeces-sor"; the paper's superscript minus is written here as T−.

Look at the prefix ladder again and this is obviously safe: the predecessor's history is a superset of the dead tail's, so promoting it can only reveal updates, never lose one. Some requests that were in flight become committed the instant the new tail takes over. Nothing that had already been acknowledged can disappear.

This is the only case where reads stop: "Tail Failure. Query and update processing are both unavailable for 2 message delivery delays while the master sends a message to the new tail and then notifies all clients of the new tail using a broadcast." (§4)

4.3 Middle failure — the one with bookkeeping

A middle server can fail holding an update its successor never received. Drop it from the chain naively and the prefix invariant breaks. So each server keeps a list of what it has forwarded but not yet seen acknowledged:

"Whenever server i forwards an update request r to its successor, server i also appends r to Sent i. The tail sends an acknowledgement ack (r) to its predecessor when it completes the processing of update request r. And upon receipt ack (r), a server i deletes r from Sent i and forwards ack (r) to its predecessor."

§3.1, Chain Replication · Failure of Other Servers. De-hyphenated: "succes-sor"; the paper subscripts the i in "Sent i".

The repair order matters, and the paper is specific: "The master first informs S's successor S+ of the new chain configuration and then informs S's predecessor S−." (§3.1) The new predecessor then replays its Sent list before it sends anything new.

master head middle S tail steady state forward u · head appends u to Sent(head) S fails here, holding u · u is at the head, never reached the tail deleting S naively would break the Update Propagation Invariant forward u · never sent repair, in this order 1 · successor first: your new predecessor is the head 2 · then predecessor: your new successor is the tail 3 · replay Sent(head) = u, before any newer request apply u u is now committed ack(u) → head deletes u from Sent(head) query processing at the tail was never interrupted · no client saw an outage
Successor first, then predecessor. Reverse those two messages and the head could forward a new update to the tail before replaying the old one, which would put the tail's history out of prefix order — the one thing the whole design depends on.

The paper's own summary of the middle case: "Middle Server Failure. Query processing continues uninterrupted. Update processing can be delayed but update requests are not lost, hence no transient outage is experienced, provided some server in a prefix of the chain that has received the request remains operating." (§4)

# the invariant every repair must preserve
history(head) ⊇ history(middle) ⊇ history(tail)

# what each failure does to it
head dies     → drop head; unforwarded, unacked requests vanish   (reads unaffected)
middle S dies → wire S− to S+; S− replays Sent(S−) first          (reads unaffected)
tail dies     → promote T−; history can only grow, never shrink   (reads pause)

5. The trade against a quorum

Two honest costs, and the paper names both.

Read throughput is capped at one machine. Adding replicas to a chain lengthens it; it does not add read capacity, because every query still lands on the tail. A quorum system with R = 1 spreads reads over all N — at the price of a weaker guarantee — and even a quorum read at R = 2 of N = 5 uses far more of the cluster than a chain ever does.

Write latency is the length of the chain. A quorum writes in parallel and waits for the slowest of W. A chain writes serially and waits for the sum:

"With parallel dissemination, the time needed to generate a reply is proportional to the maximum latency of any non-faulty backup; with serial dissemination, it is proportional to the sum of those latencies."

§4, Chain Replication

The paper's own worked figure makes the asymmetry concrete: "if a chain comprises three servers, the total latency to perform an update is 94 ms […] Query latency is only 7 ms, however." (§5.1, de-hyphenated: "pro-cess"; the elision removes the per-hop breakdown.)

DimensionChain replicationQuorum (Lesson 05)
Read pathTail only, one serverR replicas, chosen per query
Read coordinationNone — position does the workIntersection computed on every read
Read throughputCapped at one node, whatever N isScales with N / R
Read latencyOne hop to the tailSlowest of the R replicas
Write pathSerial, head to tailParallel fan-out to W
Write latencySum of the hopsMax of the W replicas
Durability of an ackEvery replica in the chain holds itW replicas hold it
GuaranteeStrong consistency, stated outright"Quorum-like"; ordering still your problem
Concurrent writesOrdered by the head, no conflictsCan both succeed and diverge
Membership changesAn external master, itself run on consensusPreference lists, sloppy quorums, hinted handoff
Slow replicaDelays writes; reads only if it is the tailCan be voted around if N exceeds R

Read the last two rows together with Lesson 05. Dynamo bent its quorum to stay writeable during failures and paid in conflicts the application had to merge. A chain refuses that bargain: it would rather pause writes for two message delays and never hand you two versions of the same object.

6. Residual risk

  • The tail is a hot spot by design. Every read and every reply leaves from it. Sizing the system means sizing that one node for the whole read load of the chain.
  • The master is the real single point of failure, and making it not one costs you a consensus protocol. The elegance of the data path is paid for on the control path.
  • Repair rebalances badly. After a server recovers and is re-added, the paper observes that it "will now participate in fewer chains than other servers but will be the tail of every chain in which it does participate. So the load is no longer well balanced over the servers, and aggregate query throughput is lower." (§5.3, de-hyphenated: "through-put".) A recovered node becomes a tail-shaped hot spot for every chain it joins.
  • Longer chain, slower writes, better durability. There is no setting of chain length that is good at both, and unlike R and W you cannot vary it per operation.
  • Everything assumes fail-stop. A server that is alive but wrong — a corrupt replica silently forwarding garbage — is outside the model, and the tail will serve that garbage with full confidence.

7. Check yourself

8. Back to your world

You are unlikely to deploy a chain. You are very likely to meet its shape: a write path that must land everywhere before it is acknowledged, and a read path deliberately pinned to one place so it needs no agreement. Object stores, replicated logs and several modern storage engines borrow exactly this.

  • Where is your read path allowed to be pinned? If one node can serve every read, you can have linearizability for free. If it cannot, you are buying a quorum whether you wanted one or not.
  • What does an acknowledgement mean in your system? "All replicas have it" and "a majority have it" are different promises, and they fail differently.
  • Who is your master, and what happens when it is wrong? Every chain has one. If yours is a health check and a DNS record, you have a master with no consensus behind it.
Ask me things. Good directions: "walk me through CRAQ, which lets every node serve reads" · "what breaks if two masters disagree about who the tail is?" · "compare an acknowledged chain write with a Raft commit" · "how long should a chain be?" · "I think I can just read from a follower. Grill me."

Read the primary source

Chain Replication for Supporting High Throughput and Availability — van Renesse and Schneider, OSDI 2004. Read §3 for the three rules, §3.1 for the Update Propagation Invariant and all three failure cases, and §4 for the honest comparison with primary/backup. Quotations above are de-hyphenated where the PDF broke a word across a line; each such case is noted in its citation.

Carry on