176 karma · joined July 12, 2011
Twitter: http://twitter.com/armon Github: http://github.com/armon Personal Site: http://armondadgar.com/
At a high level, ZooKeeper gives you a low-level distributed primitives. While these are all you need to build leader election, service discovery, monitoring, etc, it does require you to actually build that all yourself.
Consul goes the route of providing high-level features instead of low-level primitives (although they are all there, and usable via an HTTP API). It makes it easy as an operations person to deploy, and just as easy as an app developer to integrate and use.
We developed an extremely high performance server around it (hlld): https://github.com/armon/hlld.
We were typically hitting with with tens of thousands of requests per second across about 50K counters. Although it was benchmarked to >1MM ops a second.
Similarly, we also make bloomd, which is an equivalent for using bloom filters, which provide a more set-like abstraction: https://github.com/armon/bloomd
Consul is a CP system, meaning it trades availability for consistency. It has a much more limited ability to tolerate failures. However, its more central architecture allows it to support a richer feature set.
By keeping the tools separate we give developers and operators two different tools. Sometimes you need a hammer, and sometimes a screwdriver will do.
This page compares the two: http://www.consul.io/intro/vs/serf.html
We compare Consul to ZooKeeper here, but much of that applies to Cassandra as well: http://www.consul.io/intro/vs/zookeeper.html
Internally, Consul could also use something like Cassandra to store the data. However we use LMDB which is an embedded database to avoid an expensive context switch out of the process to serve requests with lower latency and higher throughput.
Using a local SQLite, I can do "update counts set val=val+1 where id=?;" ~250K times per second on a Macbook Air. It would've been simpler to just do a local aggregate and then a batch update. That avoids Redis durability issues, or a secondary merge process.
FWIW, a local Postgres can do only about 3200 increments per second.
At Amazon, you don't get any of that. It is a very dry culture. It's clear that everybody is a coworker first. Eccentricity is largely looked down on (after all you need to worry about reviews and promotions, etc). Instead you get a sort of blah corporate culture, everything is grey, you talk about frugality as if somehow that's great. Rarely do you find super motivated engineers. Most people are fairly jaded, and aren't that committed to the success of projects. It's just a 9 to 5.
I'm not saying it "won't work well". It works as it is designed to. It will be available for operations, it will automatically heal when the partition recovers, and the state will be resynchronized with the "failed" nodes. The system will be in an eventually consistent state, which is expressly documented and is it's normal mode of operation.
If you consider 5% packet loss "minimal", I'm not sure what applications you are running. TCP degrades at over 0.1% packet loss, and most UDP streaming protocols have serious degradation over 5%.
1) Send a "pre-deploy" event. Handler scripts use random number generator to decide which group they are in ("flip a coin" basically)
2) Half the nodes should transition to the "left" state, do the deploy and rejoin the cluster.
3) Once this is done, trigger the migration.
4) Flip the LB to the nodes that did the Join/Leave (you can potentially distinguish them using different role names, or by tracking who left and joined)
5) Run the "post-deploy". The other half of the nodes should now deploy
6) Update the LB to include everybody as the nodes leave/join
This is of course a rough sketch, it is certainly possible if tricky to build something like this.
tl;dr the systems is in fact designed with network errors in mind, as opposed to handling them being an afterthought.