Raft and Consensus
Five computers are asked to keep the same list. Any of them can stop working at any moment, and the wires between them can go quiet for a while and then come back. Somehow the five must never disagree about what is on the list. Raft is the set of rules that gets that done. It is short enough to build one piece at a time and watch every piece work.
There is one set of Raft rules built into this page, and every lab is a view onto it. The same election code, the same repair code and the same counting rule run whether you are stepping one moment at a time by hand or hammering a five-machine cluster with crashes. Nothing on these pages is a number typed into the prose: the clusters are really built, really run, and really checked.
No maths beyond adding and halving, and no programming. Two steps ask you to write a short rule in code, and both give you the shape to fill in and explain every word of it. If you have done Distributed Consistency you will recognise the failures being simulated here, but this course introduces each one again from the beginning.
The steps
One log, many machines, and no disagreement ever
Start with the job, because the rest of the course is one long answer to it. A bank keeps your balance on a computer. One computer is not enough: it can catch fire, or its power can fail, and your money should not depend on a single box in a single room. So the bank runs several computers, and a group of computers working together as one service is called a cluster.
None of these machines stores your balance as a number. Each one stores the log: a numbered list
of the commands that have happened, in order, where nothing is ever changed once it is written down.
+50 means money came in. -30 means money went out. Each line of the log is called
an entry, and its number is its index: entry 1, entry 2, entry 3. To find the balance, a
machine starts at 100 and applies every entry in order.
Why keep a list of changes instead of just keeping the number?
Because a number on its own cannot be checked, repaired or compared. If two machines hold the number 120 and 90, there is no way to work out which one is right. If they hold lists of commands, you can put the lists side by side and find the exact entry where they stopped agreeing.
A list of changes has another property that matters more here. Two machines that hold the same list and apply it in the same order must arrive at the same answer, every time, with no coordination at the moment of asking. So the whole problem of keeping many machines in agreement collapses into one question: can we keep their logs identical? Everything else follows from that.
So the goal is exact: keep the logs on all the machines identical, forever, while machines crash and the network misbehaves. Below, nothing enforces that yet. You are the network, and you decide who hears what.
What do +50, -30 and x2 actually do?
They are the commands in this course's toy service, and they mean what they look like. Starting from
100, a log of +50 then -30 gives 120. An x2 doubles the balance,
which is not something a real bank does, but it is here on purpose: doubling is the command that makes
order matter.
Real clusters copy real commands, and this is the honest shape of it. The program behind a shop's website copies "set row 91 to this value". A service that stores files copies "put this piece of the file at this position". The machinery in this course does not care what the commands mean, only that everybody holds the same ones in the same order.
x2 then
-30 is not the same as -30 then x2, because doubling a smaller
number gives less. So the target is stricter than it first looked: the same entries, at the same
indexes, in the same order, on every machine.Why five machines? Why not two, or a hundred?
Two is the worst number. When two machines disagree, there is no way to break the tie, and if one goes quiet the other cannot tell whether it has crashed or whether the wire has broken. Everything in this course rests on being able to gather more than half, and more than half of two is two, so a two-machine cluster stops working the moment anything goes wrong.
Odd numbers are used for that reason, and small odd numbers because every machine has to be sent every entry. Three survives one failure and five survives two, which is why five is the usual answer. A hundred machines would survive forty-nine failures and would be slow at everything, because more than half of a hundred is fifty-one replies to wait for.
+50 then -30 gives
the same answer either way round, because adding and subtracting can be done in any order. Put
x2 in the list and the order decides the money. A rule that only sometimes matters is the
most dangerous kind, because a cluster can look fine for months and then quietly get one wrong.Number the stretches of time, so a stale leader shows up
To keep the logs identical, only one machine may decide what goes in them, and that machine is called the leader. The others are followers: they take what the leader sends and write it down. A machine that is trying to become leader is a candidate. Every machine is in exactly one of those three roles at any moment. It can change role in either direction.
Leaders do not last forever. One crashes, another is elected, and later the first one comes back still believing it is in charge. So the cluster needs a way to tell an old leader from the current one. It cannot use the clock, because clocks on different machines disagree, and a machine has no way to know its own clock is wrong. Instead Raft counts. Time is divided into numbered stretches called terms: term 1, term 2, term 3, each with at most one leader. Every message carries the term number of the machine that sent it.
What is a term, exactly, and what makes it go up?
A term is just a number that each machine keeps and never lowers. It goes up in one situation: a machine gives up waiting to hear from a leader and decides to try to become leader itself. It adds one to its own term and asks for votes in that new term. So terms count leadership attempts, not seconds, and a term can last a millisecond or a month.
Two rules make the number useful. A machine that hears a term higher than its own adopts that number at once and drops back to being a follower, because a higher term means it has missed something. A machine that hears a term lower than its own refuses the message, because the sender is out of date and does not know it yet.
Two kinds of message travel between machines. One asks for a vote, which is Step 3. The other carries log entries and is called an append, which is Step 5. What matters now is only the number stamped on the outside, and the rule for reading it.
Why not just use the time of day on each machine?
Because a clock is measured and a term is counted. Measuring involves error: a machine's clock runs slightly fast or slow, gets corrected by a jump, or was set wrong when it started. Two machines a metre apart can disagree by a whole second, and neither can tell which of them is the odd one.
Counting has no error. Nothing is being read off a dial. The number was 4 and somebody added one, so it is 5, and every machine that hears about term 5 knows term 4 is over. The cost is that a term number says nothing about how long anything took. It only puts events in order, and order is all these rules need.
Why does a reply carry a term as well?
Every message carries one, in both directions, and the reply is where a leader learns it has been replaced. Suppose a leader from term 4 sends an append to a follower that has moved on to term 7. The follower refuses, and its refusal is stamped term 7. The old leader reads that number, adopts it, and steps down to being a follower before it can do any damage.
So one number does two jobs. On the way out it lets the receiver check whether the sender is current. On the way back it lets the sender find out that it is not. Nobody has to be told in words, and no machine has to keep a list of who has been leader.
Hold an election with a timeout, some votes and a majority
Nobody appoints the leader. There is no operator and no special machine, so the cluster has to pick one by itself out of a group of identical machines that can only send each other messages. Raft does it with a countdown and a vote.
Every follower runs a countdown called its election timeout. A message from the leader resets it. If the countdown reaches zero, the follower concludes that there is no leader worth waiting for. It adds one to its term, votes for itself, becomes a candidate, and asks every other machine for a vote. If it collects votes from more than half of the cluster, it is the leader. More than half is called a majority: in a cluster of five, a majority is three.
What is a tick, and where did the seconds go?
The cluster in these labs runs on ticks instead of seconds. A tick is one step of the simulation: first every message that is due gets delivered, then every machine's countdown goes down by one. A message takes a whole number of ticks to travel.
The point of ticks is that you can take one at a time and see the exact run that a fast animation would have blurred past. Nothing is waiting on a real clock, so nothing happens between your presses. In a real cluster a tick would be a few tens of milliseconds. The timeouts here of six to fourteen ticks would be a couple of hundred milliseconds of silence before somebody gets suspicious.
Why more than half, rather than some other number?
Because two groups that are each more than half of the same cluster cannot exist. Any two majorities have to share at least one machine, and that shared machine only gives one vote per term. So if a majority elected somebody in term 9, no second machine can also collect a majority in term 9. One term, at most one leader, guaranteed by arithmetic rather than by hoping.
Lower the threshold and the guarantee is gone. In a cluster of five, two groups of two fit comfortably side by side with nothing shared, so both could crown a leader. The lab below does that sum for you and then really runs the cluster to check the sum was right.
What stops the countdowns once somebody is leading?
The leader keeps talking. Every few ticks it sends an append to every follower, and when there is nothing new to send it sends one anyway with no entries in it. That empty append is called a heartbeat, and its only job is to arrive. Receiving it resets the follower's countdown, so a follower that keeps hearing from a leader never times out.
You can watch this in Lab 5's row of messages, once somebody has won. An append that says "nothing new" is a heartbeat. It also means silence is the signal for everything: a follower does not need to be told the leader has died, and could not be, because a dead machine sends nothing. It only needs to notice that the heartbeats stopped.
A split vote, and the one line of randomness that breaks it
There is a hole in Step 3. Suppose every machine waits exactly the same number of ticks. Then they all give up at the same moment, all become candidates in the same term, and all vote for themselves. Five candidates with one vote each, and a majority is three, so nobody wins. That is a split vote.
The cluster is not stuck forever: the countdowns run again and another election starts. The trouble is that if the timeouts are still identical, the next election splits in exactly the same way, and so does the one after. The term number climbs and no work gets done. The fix is one line: each machine picks its own waiting time at random from a small range, so somebody gets there first.
What does random mean here, if the same run happens twice?
Random means unpredictable to the machines themselves, not unrepeatable. Each cluster in these labs is built from a starting number called a seed, and the same seed produces the same sequence of "random" waits every time. That is why Start over gives a different run: it moves to the next seed.
Repeatable randomness is how anybody debugs a system like this. A failure that only appears once is almost impossible to study. A failure you can reproduce by naming a number can be watched as many times as you need. Real clusters use a real random source, and the property that matters is the same in both cases: machines that were identical stop being identical.
Why can nobody win a split vote, even later in the same term?
Each machine has one vote per term and may not change its mind. A candidate votes for itself the moment it becomes one, so a five-way split leaves five machines all holding a spent vote. Nobody can reach three, however long everybody waits, because there are no unspent votes left in that term.
The way out is a new term. The countdowns keep running, somebody times out again, adds one to its term, and now every machine has a fresh vote to give in a term it has not voted in. So a split vote costs a term and a round trip of messages, and that is all it costs. Nothing is corrupted by one.
Why does a candidate vote for itself, if that is what causes the deadlock?
Because the alternative is worse. Imagine a machine that held its vote back to see who else turned up. It would need a rule for how long to wait, and another for what to do if two candidates ask at once, and every such rule brings back the problem it was meant to solve. Voting for yourself needs no rule at all, and in a cluster of five it only costs you the election when at least three machines campaign at the same moment.
It also gives a small cluster something for nothing. A candidate in a three-machine cluster starts with one vote and needs only one more, so a single reply wins it. The split vote is the price, and Lab 8 measures the price as being close to nothing once the timeouts differ.
The leader appends, and the followers agree
There is a leader now, so the cluster can do some work. A client is whatever program wants something done: a cash machine, a website, an app on a phone. It sends its command to the leader, and only to the leader. Followers turn clients away.
The leader writes the command at the end of its own log, onto its disk. That is the part of a machine that keeps what it was given even after the power goes off. Then it sends an append message to every follower. Writing it down is not the same as promising it happened. The leader may only tell the client "done" once a majority of machines hold that entry, and that is committing, which Step 7 pulls apart properly. Until then the entry is written but not promised.
What is actually inside an append message?
A message is a small bundle of named values, and each named value is a field. An append
carries five of them. Three are straightforward: the leader's term, the new
entries themselves, and how far the leader has committed. The other two describe the entry
immediately before the new ones, and are called prevIndex and prevTerm.
Those last two are the interesting ones, and they are the subject of Step 6. They are not information the follower needs in order to write anything down. They are a claim about what the follower should already be holding and the follower checks the claim before it accepts a single entry.
Why send an entry out before it is promised to anybody?
Because there is no other order available. An entry can only be promised once a majority hold it, and they can only hold it if somebody sent it to them. So every entry spends a short time in a state where it exists, is written on disk in several places, and is still not something the client has been told about.
That in-between state is where nearly every interesting failure in this course lives. Step 8 crashes the leader in the middle of it. If you find yourself asking "but what if it dies right now", you are asking the right question, and the answer is a whole step.
Why does the append carry several entries and not just the newest one?
Because a follower can be a long way behind, and one entry per message would take a long time to fix that. The leader sends everything from its guess for that follower onwards. A follower that missed ten entries can be handed all ten in one message, and is caught up after a single exchange.
This is also why the check in Step 6 is about the entry before the batch rather than about each entry in it. The batch is written or refused as one piece. A follower never ends up holding half of a message, which is one fewer state for anything to go wrong in.
What a follower does when its log does not match
A follower has been away. Its log is short, or it holds entries from an older leader that the rest of the cluster never agreed to. The new leader does not know which, because a leader keeps only a guess about each follower. Something has to find the disagreement and fix it, using nothing but messages.
The rule is short, and it is called the consistency check. Before writing anything, a follower
checks the append's claim about the entry just before the new ones: index prevIndex should be
from term prevTerm. If that does not match its own log, it refuses and writes nothing at all.
The leader takes the refusal, lowers its guess by one entry, and tries again. Sooner or later the claim
lands on an entry the two really do share, the follower accepts, and anything after that point on the
follower is thrown away and replaced.
Why does checking one entry make the whole history safe?
Because the check was applied to every earlier entry too, so it reaches all the way back, one link at a time. A follower only ever accepted entry 40 after agreeing about entry 39, which it only accepted after agreeing about entry 38, and so on down to the empty log. So "we agree about entry 40" carries the whole of the history with it.
That is why one small comparison is enough, and why it has to be done before writing rather than afterwards. Accept an entry without the check and the chain is broken forever: from then on the two logs can differ anywhere below, and no later message would ever notice.
I have not written code before: what is a function, and what is true or false?
A function is a named piece of code that takes some values in and hands one value back. In the
lab below, accepts is handed three things and must hand back either true or
false. Those two words are the only answers a yes-or-no question has in code, and a
follower's decision is exactly a yes-or-no question.
One of the three values handed in is a list: several values in order under one name, which
programmers usually call an array. myTerms is the list of terms of the entries the follower
already holds, so myTerms[0] is entry 1's term and myTerms[1] is entry 2's
term. The number in the brackets is the index, counted from zero, which is why entry 5 lives at
myTerms[4]. That off-by-one is a real nuisance and everybody meets it.
Four more pieces of writing and you have everything the lab needs. return x means "hand
back x and stop". if (test) { … } means "do the part in the curly brackets
only when the test holds". === asks "are these two exactly equal", and it is three equals
signs because one equals sign means something else. And myTerms.length is how many entries
the list holds, so a claim about an index bigger than that is a claim about an entry the follower does
not have.
Is backing up one entry at a time not painfully slow?
It can be. A follower that is a thousand entries out of step costs a thousand refusals, each one a round trip across the network, and Lab 11 lets you feel it at a small scale. The reason it is acceptable is that it is rare: this only happens after a leader change, and only for a follower that was away.
Real implementations speed it up without changing the idea. A refusing follower also reports the first index it holds from the term that clashed. That lets the leader skip a whole term's worth of guesses in one step instead of one entry at a time. The safety argument does not depend on the speed, so this is a speed-up rather than a different mechanism, and it is safe to leave out until the log gets long.
Decide when an entry is safe to act on
An entry in a log is a written note. A committed entry is a promise: the cluster will never lose it, never reorder it, and never put something else at that index, whatever crashes next. The rule is a count. When a majority of the machines hold the entry, the leader commits it, tells the client yes, and the balance may be reported as including it.
The leader keeps track of how much of the log each follower has, from the replies coming back, and looks for the highest index that a majority hold. Notice what it does not do. It does not take the largest number, and it does not trust any single machine, including itself.
What does committed actually promise, and to whom?
It promises the client that asked. Before an entry is committed, a client that asks "did my transfer happen" must be told no, or told nothing at all. After it is committed, the answer is yes, and no later event may turn that yes back into a no. Not a crash, not a network split, not an election.
That is why the count is a majority and why entries are on disk before they are counted. Any future majority must share at least one machine with this one, and that shared machine is holding the entry. So the entry cannot be lost by any group that is large enough to be allowed to decide anything.
How do you count how many machines hold entry number n?
You walk the list and count. For a given n, look at each machine's total in turn. Add
one to a running count every time that total is n or more. Then compare the count with the
majority you need. That is what a loop in the code lab below does: repeat the same small piece of work
once per machine.
To find the highest such n, do that for n equal to 1, then 2, then 3, and
remember the largest one that reached a majority. It is not the fastest way and it is the clearest, and
the whole list is at most a few dozen entries, so clearest wins.
The lab writes that out with four more pieces of code. let best = 0 gives a name to a
value that is going to change; const need = … gives a name to one that will not.
for (let n = 1; n <= 40; n = n + 1) is the loop: run the part in the curly brackets with
n as 1, then as 2, and so on up to 40. for (const s of stored) is the same
idea over a list: run it once for each machine's total, calling that total s. And
s >= n asks "is s at least n", while
Math.floor rounds a number down to a whole one, which is how you halve five and get two.
Does the leader count itself?
Yes, and it must. The leader is one of the five machines and it is holding the entry on its own disk, so it is one of the copies. In a cluster of five, a leader plus two followers is three machines, which is the majority, so two replies are enough. That is why the count in Lab 13 starts at one before any follower is added.
Counting itself is not the same as trusting itself. It contributes one, exactly like everybody else, and one is never a majority of five. The rule is doing arithmetic over the whole cluster and the leader has no special weight in it beyond being the machine that does the sum.
A leader crashes in the middle of an append
Now the interesting part. A leader takes a command, writes it as entry 4, starts sending it out, and dies before the story finishes. Depending on the exact moment, the entry might be on nobody else, on one other machine, or on three. Only the last of those was ever promised to a client.
Whatever happened, the surviving machines notice the silence, hold an election and carry on. The question is what happens to entry 4. It has to survive if it was committed, and it is allowed to disappear if it was not, because in that case nobody was ever told anything about it.
What survives a crash, and why does the log come back?
A machine has two kinds of memory. Working memory is fast and forgets everything when the power goes. The disk is slower and keeps what it was given after a restart. Raft writes a log entry to disk before it answers for it, so a crashed machine comes back holding the same log and the same term it had when it fell over.
This is not a detail. The whole safety argument counts machines that hold an entry. A machine that forgets on restart would make the count meaningless, because an entry on three machines could be on zero after a power cut. The order matters too. Write first, answer second, because answering first and writing second is a promise that can evaporate.
Why can a new leader not just commit an old entry by counting it?
Because a count taken now can be undone by a machine that is currently switched off. An entry from an older term can sit on a majority of the machines that happen to be up. A machine that is down may be holding a longer log from that same period, and when it comes back it is entitled to replace that entry. Committing it would be a promise that a later election could break.
So Raft adds one condition to Step 7's rule: a leader only commits by counting when the entry was created in its own current term. Old entries come along for free. The moment a new leader gets one entry of its own onto a majority, everything below it is committed too, because a log is only ever accepted whole. That is what the second button in the lab above demonstrates.
One more rule earns its place here, and it is called the up-to-date rule. When machines vote, they do not vote for just anybody. A machine refuses a candidate whose log is behind its own. Behind means one of two things: the candidate's last entry is from an earlier term, or it is from the same term and the candidate's log is shorter. So a machine that missed the last few entries can never be elected over machines that have them.
Why does a machine that restarts come back as a follower?
Because it has no idea how long it was gone. A machine that woke up still calling itself leader would start sending appends stamped with an old term, into a cluster that may have held three elections while it was down. Coming back as a follower costs nothing. If there is a current leader it will hear from it within a few ticks, and if there is not, its countdown runs out and it can campaign like anybody else.
The term and the log come back from disk, so it is not starting from nothing. It keeps everything it knew and gives up only the claim to be in charge, which is the one thing it knew that could have gone stale while it was away.
A split with two leaders, and why only one of them counts
Crashes are the easy failure, because a crashed machine stays quiet. The hard failure is a network that breaks in half while every machine keeps running. Each side sees the other side go silent and cannot tell whether those machines died or whether a wire came loose. This is called a split.
Cut five machines into a group of two and a group of three. The side of three hears nothing from the leader, times out, holds an election and elects somebody, because three is a majority. The old leader is over on the side of two, hears nothing to contradict it, and carries on calling itself leader. Two leaders at once, which sounds like the disaster this whole course exists to prevent.
What splits a network in half in real life?
Ordinary things. The box that wires one row of machines to the next stops working. Somebody sets a rule meant to keep strangers out, and it keeps their own machines out instead. A cable is unplugged while somebody is tidying. The long link between two buildings gets so busy that nearly everything sent along it is dropped. From inside a machine, all four look identical to the other machines having died.
That indistinguishability is the point, and it is why "just check whether the other one is alive" never works. No message can tell you the difference, because the whole problem is that messages are not arriving. Raft never tries to tell the two apart, which is why it survives both.
Why does the stranded leader not notice it has lost?
Because nothing tells it. It sends appends into the silence and gets no replies, which is exactly what a busy or briefly congested network looks like. There is no message meaning "you were replaced", and there could not be one, since it would have to travel across the very link that is broken.
It finds out the moment the link comes back, and it finds out from one number. Any message it receives carrying a higher term makes it drop to being a follower at once. Until then it keeps trying, which is harmless, because trying is not the same as committing.
+50
to the small side, twice. The entries appear on that leader's log and stay without check marks
forever. Then press the button that sends -30 to the big side and watch those entries
commit within a tick or two.Why does the stranded side keep holding elections it cannot win?
Because it cannot tell that it is the stranded side. From inside those two machines, the picture is identical to three machines having burned down, and in that situation trying to elect somebody is exactly the right response. A rule like "stop trying after five failures" would leave a cluster leaderless after a genuine outage, which is worse than a machine wasting messages.
The cost of trying is small and bounded: a term number that climbs and a few messages that go nowhere. The moment the link comes back, those higher term numbers are actually useful, because they push the cluster into a fresh election that everybody can take part in.
Rejoining, and watching the loser's log be overwritten
Nothing has been broken yet, but something is untidy. The stranded leader from Step 9 is holding entries that the rest of the cluster never agreed to, and the rest of the cluster has moved on with entries of its own. Both sides believe their own log. That is the state a real cluster is in the moment a broken link comes back.
Joining the network back up is enough to fix it, and the mechanism is one you already built. Step 6's consistency check finds the first index where the two logs disagree and the loser's entries from that point on are thrown away and replaced by the winner's.
Is throwing away entries not exactly the disaster we were avoiding?
It would be, if any of them had been committed. They were not. The stranded leader could only reach one other machine, so nothing it wrote ever sat on three of the five, so no client was ever told that any of it had happened. Discarding an entry nobody was promised costs nobody anything.
The rule from Step 8 is what makes this safe rather than lucky. A machine only wins an election if its log is at least as up to date as a majority of the cluster, so the winner already holds every committed entry there is. Anything it does not hold was, by that argument, never committed. The overwrite can only ever destroy notes, never promises.
What a real cluster does that this one skips
Two things, and both are engineering rather than new ideas. A log that only grows would eventually fill any disk, so real implementations take a snapshot of the state now and then and throw away the entries that led up to it. A machine that is far behind is then sent the snapshot instead of ten million entries.
The other is changing the membership: adding a machine or removing one without ever having two different majorities that do not overlap. Raft handles it by moving through a combined configuration where a decision needs a majority of both the old and the new set. Neither changes anything you have built here, and both are safe to leave until you need them.
Which log wins, and who decides?
The leader's log wins, always, and nobody decides anything at the moment of the merge. There is no comparison of the two histories, no rule about which looks better, and no vote about it. A follower's only move is to say yes or no to a claim, and once it says yes, everything after that point is replaced by whatever the leader sends.
All the judgement happened earlier, at the election. Choosing a leader whose log is at least as up to date as a majority is what makes "the leader's log wins" a safe rule instead of a reckless one. Get the election right and the merge needs no cleverness at all, which is the part of Raft worth copying into other designs.
Run a five-machine cluster, and try to break it
Everything is built. You have elections with random timeouts and appends with a consistency check. You have repair by backing up one entry at a time, commit by counting a majority, and the up-to-date rule that decides who may be elected. Put them together and you have a cluster that can be attacked.
Below is the whole thing, with a safety check that really reads every log after every tick. It looks for two leaders in one term, two machines that have committed different commands at the same index, and two logs that disagree at an index where both claim the same term. There is a list of five things to try, and one of them is not achievable.
What the safety check actually reads
It walks every machine's log and compares it against every other machine's, entry by entry, at each index they both reach. It also keeps a record of what was committed at each index the first time anything committed there, and compares every later commit against that record. The panel reports how many comparisons it just did, so you can see it is doing work rather than displaying a reassuring word.
Checking history as well as the present matters. A cluster that committed +50 at index 4
and then quietly replaced it with -30 would look perfectly consistent at any single moment,
because all five machines would agree on the wrong thing. Only a check that remembers can catch it.
Why can I not make a committed entry disappear, however hard I try?
Because of two rules working together. A committed entry is on a majority of the machines. A machine can only be elected with votes from a majority, and no machine votes for a candidate whose log is behind its own. Any two majorities share at least one machine, and that machine is holding the entry, so it refuses anybody who does not have it.
So every leader that can ever exist already holds every committed entry, which means no leader can ever order that index overwritten. The argument uses no timing, no assumption about how fast messages travel, and no trust in any single machine. That is why it holds under any attack you can run in the panel above.
What Raft does not promise
It does not promise the service keeps working. Crash three of the five machines in the panel above and the cluster stops accepting anything at all, correctly and on purpose, because no majority exists to promise with. Raft protects the truth of what it has already said, and it would rather stop than say something it might have to take back.
It does not promise speed either. Every command waits for a round trip to a majority, so a cluster spread across the world is slower than one in a single room, and no amount of tuning changes that. What you get in exchange is the one thing you cannot build afterwards: an answer of yes that stays true.
What you can do now
- Explain why a group of machines keeps a log of commands rather than a stored answer.
- Use a counter instead of a clock to put events from different machines in order.
- Say why a leader needs more than half the votes, and what breaks at exactly half.
- Break a deadlock between identical machines with one line of randomness.
- Write the consistency check a follower applies before accepting an append.
- Work out how far a leader may commit, by counting rather than by trusting.
- Tell a written entry from a promised one, and know which one may be thrown away.
- Attack a running cluster with crashes and splits, and say why the promises hold.
Where this goes
- Real systems that run this. The rules you just built are the ones behind etcd, which keeps the configuration of a Kubernetes cluster, and behind Consul, TiKV and CockroachDB. When one of those elects a new leader in a few hundred milliseconds, this is what happened.
- Inside a Database. A log that must survive a power cut, applied in order after a crash, is the write-ahead log of a database engine. The same idea one layer down, on one machine.
- Build a Query Engine. Once several machines agree on the data, somebody has to answer questions about it quickly, which is a different kind of hard.
Persist before replying
A server's current term, its vote and new log entries are hard state. They must survive a restart. A voter that replies before its vote is durable can reboot and vote twice in one term. A follower that acknowledges an append before the entry is durable can make a leader count data that disappears in a power cut.
The integration boundary therefore has an order: write hard state, entries and any snapshot to durable storage; wait for the storage completion; only then send vote or append responses that depend on it. Committed entries are applied to the state machine in order. Applying before durability or exposing an uncommitted result breaks the promise.
What “durable” means here
The storage layer must define when a completed write survives the failures in scope. A write accepted by an operating-system cache is not automatically safe from power loss.
Do a client command once
A client can lose the leader's reply after its command commits. Retrying the same transfer as a new command would execute it twice. Give each client a stable session ID and each command an increasing request number. Replicate the latest number and result with the state-machine update, so every future leader can recognise a duplicate and return the saved result without applying the command again.
This produces one application effect for retries inside the retained session history. It does not make an email, card charge or other external side effect atomic with the Raft log. Those systems need their own stable idempotency key and durable effect record. Expiring a client session also expires part of the deduplication promise.
Why the result is stored too
A duplicate request must receive the same logical answer without running again. Remembering only that a number was seen prevents duplication but cannot reproduce the response.
Serve a linearizable read
A machine can still think it is leader after losing contact with the majority. Reading its local state is fast but may be stale. One safe path puts a no-op in the new leader's term, confirms leadership with a quorum, obtains a read index and waits until the local state machine has applied through that index before answering.
A lease-based read can avoid the quorum exchange, but its safety depends on bounded clock drift and active quorum checking. A clock pause or backward step can extend a lease beyond reality. Label stale reads, quorum-confirmed reads and lease reads as different APIs rather than hiding the trade-off behind one “read” button.
Why committing in the current term matters
A new leader first commits an entry from its own term before using the usual read-index reasoning. That establishes which prior entries are committed under Raft's current-term rule.
The current etcd Raft implementation documents quorum-confirmed and lease-based read paths.
Compact and install a snapshot
An ever-growing log is a recovery record, not a practical storage plan. After the application has applied through index k, it can save a snapshot of state plus the last included index and term. Only then may earlier log entries be discarded. The snapshot must also contain replicated client-session and membership state needed after recovery.
A follower behind the compacted prefix cannot be repaired with ordinary AppendEntries. The leader installs a snapshot, the follower verifies and persists it, restores the state machine, then continues with entries after the snapshot boundary. An interrupted transfer must not leave a half-snapshot presented as complete.
Snapshot versus backup
A Raft snapshot accelerates replica recovery and log compaction. It is not automatically an independent, historical or disaster-recovery backup; all replicas can faithfully agree on corrupted application state.
Change membership safely
Changing five voters to a different five in one step can create two disjoint majorities, each able to decide. Joint consensus passes through a configuration where decisions require majorities of both the old and new voter sets. The overlap prevents two independent committed histories during the transition.
Add a new server as a non-voting learner first, let it catch up, then promote it through the configuration-change protocol. Remove failed members only while the current configuration can still commit the removal. A two-member cluster that loses one member cannot safely vote the missing member out; this is one reason production groups normally use at least three voters.
Why one-at-a-time changes can also work
Some implementations preserve quorum overlap by allowing one voter change at a time and rejecting another while one is uncommitted. Follow the exact membership protocol your implementation proves, not a mixture of two designs.
The extended Raft paper derives joint consensus; etcd Raft documents its one-at-a-time implementation variant.
Reduce election disruption
A partitioned follower can repeatedly time out and increase its term. When it rejoins, its larger term makes a healthy leader step down even though the follower cannot win. Pre-vote asks peers whether an election would succeed before incrementing the term. The isolated node cannot collect enough pre-votes, so it does not disturb the cluster merely by returning.
Check-quorum makes a leader step down after it cannot confirm an active majority. Leadership transfer deliberately moves leadership to a caught-up target instead of waiting for competing timeouts. These are liveness and operational extensions; the core election and log safety rules still carry the proof.
Pre-vote does not elect a leader
It is a permission check for starting the real election. A successful pre-candidate still increments the term, requests actual votes and must win a majority under the normal rules.
Check the state machine
Protocol tests should generate sequences of messages, timeouts, storage completions, crashes, restarts, snapshots and membership changes. After every transition, check invariants: one vote per term, log matching, leader completeness, state-machine safety, monotonic commit and apply indexes, and no response before its required durable state.
Deterministic simulation makes a failing schedule replayable. Model checking explores small state spaces and can reduce a long failure to a short counterexample. Production traces add terms, indexes, message types, peer IDs, durable/applied positions and configuration IDs. AI can prioritise unusual schedules or summarise traces, but the executable model and invariant failure are the authority.
What to observe in production
Track leader changes, proposal and commit latency, applied lag, fsync latency, snapshot size and duration, peer progress, rejected proposals, quorum loss and configuration changes. Alert on user impact and endangered safety assumptions, not term changes alone.
Continue from consensus
- Inside a Database covers durable pages, indexes and local transaction recovery.
- Fault Tolerance connects quorum loss and recovery to SLOs and rehearsals.
- Distributed Consistency defines the client-visible histories this log is meant to support.