Recently our consul and nomad clusters both blew up within a day of each other.
In some crazy twist of luck, Amazon removed the underlying instances that happened to run the leaders to both clusters. This was in our QA environment so they were all t2.nanos.
In this situation, shouldn’t we just expect the other two nodes to hold an election and elect a new leader? Isn’t this the most plain vanilla use case there is?
In both situations, the clusters got stuck without leaders indefinitely because they got stuck trying to ping the leader to see if they could hold an election (how could they?? the whole point of the election is because the leader disappeared). And the only way to recover was to do some insane dance of building a JSON file and hard coding IPs.
Based on some research, it seems like we need to hard code IPs to avoid this in the future. This seems like a huge smell and goes against everything I’ve ever read about raft and the idea of self healing clusters.
What am I missing here? I don’t remember ever having to deal with this with mongodb in 2014.
The document supports what you were expecting. So... I too am curious what happened to you.
It sounds like your team had hard coded IPs and when the consensus was lost, they had to manually edit the peers.json file to remove the failed instance.
Also, how many consul servers were running? If it's an even number, the odds of getting into a split-brain scenario is high.
If you know any devops consultants that have solid experience with consul/other hashicorp tools, we’d love to talk to them! (US only)
That's because 2nd node failure probablity for 4 node system is P(a) + P(b) + P(c) = 3P and for 3 node system is P(a) + P(b) = 2P; it's 3P vs 2P probablity.
It's more than 50% overall because similar probability happens for first node failure - it's more likely that one out of 4 nodes will fail than one out of 3.
In other words you have to guarantee one more node running without any reliability gains and that guarantee costs reliability.
That is, if having an even number of nodes causes problems, you'll have problems every time one of your odd number of nodes goes down!
The answer is 3 is no less reliable then 4.
Even for the uninitiated, the content is surprisingly digestible. The comparison and a brief description of the two begins in section 3.
Raft looks good next to MultiPaxos, but MultiPaxos is hardly the state of the art in the mainline Paxos lineage. MultiPaxos was published in 2001, over a decade before Raft. EPaxos is more of a contemporary to Raft, and arguably more in the spirit of Paxos as originally conceived--truly distributed, leaderless consensus.
[1] Practically every paper discussing Raft, including the original Raft paper(s), aliases Paxos to MultiPaxos.
These days I think Paxos refers both to what Lamport thought of as single decree Paxos, or Paxos leader election, and also the lineage of protocols that derive from it, as well as sometimes specifically MultiPaxos. But it’s OK, words can have multiple meanings.
For what it’s worth even EPaxos is long in the tooth now. There have been several advancements in the intervening years, the latest being Accord[1], a protocol the Cassandra community has developed for cross shard distributed transactions.
[1] https://cwiki.apache.org/confluence/download/attachments/188...
(Full disclosure: I’m one of the authors)
https://vadosware.io/post/paxosmon-2-the-journey-continues/
I haven't read any papers recently but if you know of some that are worth giving a read, please let me know!
I don't find multi paxos appealing because it feels like a step back from leaderless vanilla paxos. I have been itching to implement a neat consensus protocol that's a cut above the common ones and has better throughout than etcd. Unfortunately I haven't found anything
[1] https://cwiki.apache.org/confluence/download/attachments/188...
Also supposedly Heidi Howard was working on a consensus protocol appropriate for geographically replicated datacenters, which have fast almost always reliable networks intra-datacenter but fallible slow networks inter-datacenter... That sounds amazing to me.
But yeah, something in this space would be very interesting. For example Kubernetes and nomad deploy multiple container pods per machine. Instead of sending heartbeats from each pod it would make sense to batch heartbeats of all pods in each machine and send a single beat instead.
do you have any examples handy? would like to explore that direction.
This is both recent enough that you've probably not seen it and extremely relevant to what you're interested in!
RAFT is more within the tradition and family of Viewstamped Replication than Multi-Paxos, and VSR has some optimizations beyond RAFT that are interesting, especially where a real-world storage fault model is at play.
The talk I gave linked above goes into 2012 VSR and disk persistence in much more detail, taking into account our experience implementing VSR for TigerBeetle: https://www.tigerbeetle.com
You may also especially enjoy these two recent back-to-back interviews with Brian Oki and James Cowling, paying tribute to the pioneering protocol and especially the people behind it, with Barbara Liskov at the center connecting 1988 and 2012: https://youtu.be/_Jlikdtm4OA?t=708
The interviews are a fascinating look at the history of consensus, and the background around the design decisions that went into the protocol over the years. James Cowling also shares some details of his experience leading the Magic Pocket storage infrastructure team at Dropbox that moved Dropbox off AWS.
https://github.com/hashicorp/raft
Example application: https://github.com/Jille/raft-grpc-example
I suppose one can have a subset of a cluster participating in Raft, but then the "raft group membership" itself is a consensus problem isn't it?
It's all turtles...
Yes, this is discussed briefly in the article and more thoroughly in the original Raft paper.
It's possible to do a "membership change" operation, whereby the replicas agree to change the set of nodes in the cluster. It's slightly trickier than achieving consensus on an ordinary state machine operation, because you have to ensure that quorums from both the old and new node lists agree to the change.
With this feature, you can make a "self-healing" cluster. Say you have a pool of 100 physical machines, with 5 of them running Raft nodes. If one node fails, and the failure appears to be transient, you can just wait for it to come back up. Otherwise, you can start a new node, wait for it to come up, and perform a membership change to swap it for the failed one.
This provides availability as long as you don't have a burst of more than N/2 failures faster than the cluster can recover.
I've only worked with variants of paxos which were greatly simplified, and wondered how Raft actually simplified things here.
I feel like Raft, despite being easier to explain quickly, really doesn't seem much simpler than a basic explanation of Paxos. But perhaps that's because of what I've already worked with.
I've got an itch to explore Viewstamped Replication as well...
This was only really feasible because the value under consensus was very small (far less than 1MB).
In a system where the value(s) are much larger, I think what you've proposed as a system of Raft clusters hosting shards makes a lot of sense too.
Thanks for the considerate answer!
Preferably in Go.
In the otherwise very good figure 2:
>If successful: update `nextIndex` and `matchIndex` for follower
but it is never stated how either `matchIndex` or `nextIndex` should be updated (on successful response). In section 5.3 it's only stated that "Eventually `nextIndex` will reach a point where the leader and follower logs match." I went to the TLA spec in Diego's github repo for his dissertation and found the answer to be
HandleAppendEntriesResponse(i, j, m) ==
/\ m.mterm = currentTerm[i]
/\ \/ /\ m.msuccess \* successful
/\ nextIndex' = [nextIndex EXCEPT ![i][j] = m.mmatchIndex + 1]
/\ matchIndex' = [matchIndex EXCEPT ![i][j] = m.mmatchIndex]
but this is predicated on followers responding with `mmatchIndex`, which is not included in the response as stated in the "In Search of" paper.I discovered this while TAing a class on consensus protocols, during which I built an implementation (in rust) and though I was confused about `matchIndex` as well, I just did what I thought was the natural thing, i.e. on receipt of a successful `AppendEntries` response I do
*(self.leader_state.next_index.get_mut(node_id)) = self.get_last_log_index() + 1;
*(self.leader_state.match_index.get_mut(node_id) = self.get_last_log_index();
I emailed Diego to find out if this was a reasonable solution but he told me he didn't remember the protocol clearly enough to advise. Nice to know everyone forgets things sometimes :)I am probably overthinking it and should just keep it simple and have the leader produce "wasted" entries until it realises that there is another accepted leader.
I tried implementing Raft without looking at any existing implementations, but that is probably not a good idea. It's very easy to get lost in the weeds.
I think one mistake I made is trying to run the different parts of the protocol in parallel, i.e. processing a message, accepting messages, sending heartbeats etc. It probably makes more sense for the raft part to basically be single threaded.
Yep. It's annoying to code it this way but you have to. One way to make this relatively clean is via throwing and catching exceptions if your language has good support for it.
Also agree re single-threadedness. In some ways raft assumes a single state machine loop running on a node at any point. Concurrency introduces additional non determinism to the state machine and isn't really accounted for in the formal spec.
I've heard great things about etcd source and how clean it is. I've never studied it but it might give you some pointers on best practices.