Distributed Consensus is everywhere! Even if not obvious at first, most apps nowadays are distributed systems, and these sometimes have to "agree on a value", this is where consensus algorithms come in. In this session we'll look at the general problem and solve a few example cases using the RAFT algorithm implemented using Akka's Actor and Cluster modules.
Enhancing User Experience - Exploring the Latest Features of Tallyman Axis Lo...
Distributed Consensus A.K.A. "What do we eat for lunch?"
1. Konrad 'ktoso' Malawski
Distributed Consensus
“What do we eat for lunch?”
GeeCON 2014 @ Kraków, PL
A.K.A.
Konrad `@ktosopl` Malawski
2. Konrad 'ktoso' Malawski
Distributed Consensus
GeeCON 2014 @ Kraków, PL
A.K.A.
“What do we eat for lunch?”
real world
edition
Konrad `@ktosopl` Malawski
8. What is this talk about?
The network.
!
How to think about distributed systems.
!
Some healthy madness.
Code in slides covers only “simplest possible case”.
12. Consensus - formal
Termination
Every correct process decides some value.
!
Validity
If all correct processes propose the same value v,
then all correct processes decide v.
!
Integrity
If a correct process decides v,
then v must have been proposed by some correct process.
!
Agreement
Every correct process must agree on the same value.
17. Distributed system definition
A distributed system is one in which the failure
of a computer you didn't even know existed
can render your own computer unusable.
— Leslie Lamport
http://research.microsoft.com/en-us/um/people/lamport/pubs/distributed-system.txt
18. Distributed system definition
A system in which participants communicate
asynchronously using messages.
http://research.microsoft.com/en-us/um/people/lamport/pubs/distributed-system.txt
23. Distributed Systems - failure detection
Failure detection:
• can only rely on external knowledge
• but what if there’s no-one to tell you?
• thus: must be in-some-way time based
25. Two Generals Problem
Yellow and Blue armies must attack Pink City.
They must attack together, otherwise they’ll die in vain.
Now they must agree on the exact time of the attack.
!
They can only send messengers, which Pink may intercept and kill.
39. 2 Generals translated to Akka:
Akka Actors implement the Actor Model:
!
Actors:
• communicate via messages
• create other actors
• change their behaviour on receiving a msg
!
40. 2 Generals translated to Akka:
Akka Actors implement the Actor Model:
!
Actors:
• communicate via messages
• create other actors
• change their behaviour on receiving a msg
!
Gains?
Distribution / separation / modelling abstraction
41. 2 Generals translated to Akka:
case class AttackAt(when: Date)
Presentation–sized–snippet = does not cover all cases
42. 2 Generals translated to Akka:
!
!
class General(general: Option[ActorRef]) extends Actor {!
!
! val WhenIWantToAttack: Date = ???!
!
general foreach { _ ! AttackAt(WhenIWantToAttack) }!
!
def receive = {!
case AttackAt(when) =>!
println(s”General ${otherGeneralName} attacks at $when”)!
! ! ! println(s”I must confirm this!")!
!
sender() ! AttackAt(when)!
}!
!
def otherGeneralName = !
! ! ! if(self.path.name == “blue")!“yellow" else "blue"!
}!
Presentation–sized–snippet = does not cover all cases
43. 2 Generals translated to Akka:
!
!
class General(general: Option[ActorRef]) extends Actor {!
!
! val WhenIWantToAttack: Date = ???!
!
general foreach { _ ! AttackAt(WhenIWantToAttack) }!
!
def receive = {!
case AttackAt(when) =>!
println(s”General ${otherGeneralName} attacks at $when”)!
! ! ! println(s”I must confirm this!")!
!
sender() ! AttackAt(when)!
}!
!
def otherGeneralName = !
! ! ! if(self.path.name == “blue")!“yellow" else "blue"!
}!
Presentation–sized–snippet = does not cover all cases
44. 2 Generals translated to Akka:
!
!
class General(general: Option[ActorRef]) extends Actor {!
!
! val WhenIWantToAttack: Date = ???!
!
general foreach { _ ! AttackAt(WhenIWantToAttack) }!
!
def receive = {!
case AttackAt(when) =>!
println(s”General ${otherGeneralName} attacks at $when”)!
! ! ! println(s”I must confirm this!")!
!
sender() ! AttackAt(when)!
}!
!
def otherGeneralName = !
! ! ! if(self.path.name == “blue")!“yellow" else "blue"!
}!
Presentation–sized–snippet = does not cover all cases
45. 2 Generals translated to Akka:
!
!
class General(general: Option[ActorRef]) extends Actor {!
!
! val WhenIWantToAttack: Date = ???!
!
general foreach { _ ! AttackAt(WhenIWantToAttack) }!
!
def receive = {!
case AttackAt(when) =>!
println(s”General ${otherGeneralName} attacks at $when”)!
! ! ! println(s”I must confirm this!")!
!
sender() ! AttackAt(when)!
}!
!
def otherGeneralName = !
! ! ! if (self.path.name == “blue")!"yellow" else "blue"!
}!
Presentation–sized–snippet = does not cover all cases
46. 2 Generals translated to Akka:
val system = ActorSystem("two-generals")!
!
val blue = !
system.actorOf(Props(new General(general = None)), name = "blue")!
!
val yellow = !
system.actorOf(Props(new General(Some(blue))), name = "yellow")!
The blue general attacks at 13:37, I must confirm this!!
The yellow general attacks at 13:37, I must confirm this!!
The blue general attacks at 13:37, I must confirm this!!
...
Presentation–sized–snippet = does not cover all cases
48. 8 Fallacies of Distributed Computing
1. The network is reliable.
2. Latency is zero.
3. Bandwidth is infinite.
4. The network is secure.
5. Topology doesn’t change.
6. There is one administrator.
7. Transport cost is zero.
8. The network is homogeneous.
Peter Deutsch “The Eight Fallacies of Distributed Computing”
https://blogs.oracle.com/jag/resource/Fallacies.html
71. 2PC translated to Akka
case class Prepare(value: Any)!
case object Commit!
!
sealed class AcceptorStatus!
case object Prepared extends AcceptorStatus!
case object Conflict extends AcceptorStatus!
!
Presentation–sized–snippet = does not cover all cases
72. 2PC translated to Akka
case class Prepare(value: Any)!
case object Commit!
!
sealed class AcceptorStatus!
case object Prepared extends AcceptorStatus!
case object Conflict extends AcceptorStatus!
!
Presentation–sized–snippet = does not cover all cases
73. 2PC translated to Akka
class Proposer(acceptors: List[ActorRef]) extends Actor {!
var transactionId = 0!
var preparedAcceptors = 0!
!
def receive = {!
case value: String =>!
transactionId += 1!
acceptors foreach { _ ! Prepare(transactionId, value) }!
!
case Prepared =>!
preparedAcceptors += 1!
!
if (preparedAcceptors == acceptors.size)!
acceptors foreach { _ ! Commit }!
!
case Conflict =>!
! ! ! ! ! context stop self!
}!
}!
Presentation–sized–snippet = does not cover all cases
74. 2PC translated to Akka
class Proposer(acceptors: List[ActorRef]) extends Actor {!
var transactionId = 0!
var preparedAcceptors = 0!
!
def receive = {!
case value: String =>!
transactionId += 1!
acceptors foreach { _ ! Prepare(transactionId, value) }!
!
case Prepared =>!
preparedAcceptors += 1!
!
if (preparedAcceptors == acceptors.size)!
acceptors foreach { _ ! Commit }!
!
case Conflict =>!
! ! ! ! ! context stop self!
}!
}!
Presentation–sized–snippet = does not cover all cases
75. 2PC translated to Akka
class Proposer(acceptors: List[ActorRef]) extends Actor {!
var transactionId = 0!
var preparedAcceptors = 0!
!
def receive = {!
case value: String =>!
transactionId += 1!
acceptors foreach { _ ! Prepare(transactionId, value) }!
!
case Prepared =>!
preparedAcceptors += 1!
!
if (preparedAcceptors == acceptors.size)!
acceptors foreach { _ ! Commit }!
!
case Conflict =>!
! ! ! ! ! context stop self!
}!
}!
Presentation–sized–snippet = does not cover all cases
76. 2PC with ResumeProposer in Akka
case class Prepare(value: Any)!
case object Commit!
!
sealed class AcceptorStatus!
case object Prepared extends AcceptorStatus!
case object Conflict extends AcceptorStatus!
case class Committed(value: Any) extends AcceptorStatus!
Presentation–sized–snippet = does not cover all cases
77. 2PC with ResumeProposer in Akka
!
class ResumeProposer(!
proposer: ActorRef, !
acceptors: List[ActorRef]) extends Actor {!
!
context watch proposer!
!
var anyAcceptorCommitted = false!
!
def receive = {!
case Terminated(`proposer`) =>!
println("Proposer died! Try to finish the transaction...")!
acceptors map { _ ! StatusPlz }!
!
case _: AcceptorStatus =>!
// impl of recovery here!
}!
}
Presentation–sized–snippet = does not cover all cases
98. Paxos: a high-level overview
JavaZone had a full session on Paxos already today…
99. A few Paxos whitepapers
"Reaching Agreement in the Presence of Faults” – Lamport, 1980
…
“FLP Impossibility Result” – Fisher et al, 1985
“The Part Time Parliament” – Lamport, 1998
…
“Paxos made Simple” – Lamport, 2001
“Fast Paxos” – Lamport, 2005
…
“Paxos made Live” – Chandra et al, 2007
…
“Paxos made Moderately Complex” – Rennesse, 2011 ;-)
128. Multi Paxos
• Keeps the Leader
• Clients find and talk to the Leader
• Skips Phase 1, in stable state
• 2 delays instead of 4, until learning a value
130. Raft – inspired by Paxos
Paxos is great.
Multi-Paxos is great, but no “common understanding”.
!
!
Raft wants to be understandable and just as solid.
"In search of an understandable consensus protocol" (2013)
131. Raft – inspired by Paxos
!
!
• Leader based
• Less processes than Paxos
• It’s goal is simplicity
• “Basic” includes snapshotting / membership
132. Raft - summarised on one page
Diego Ongaro & John Ouserhout – In search of an understandable consensus protocol
161. Raft Heartbeat using Akka
sendHeartbeat(m)!
log.info("Starting hearbeat, with interval: {}", heartbeatInterval)!
setTimer(HeartbeatName, SendHeartbeat, heartInterval, repeat = true)!
akka-raft is a work in progress community project – it may change a lot
162. Raft Heartbeat using Akka
sendHeartbeat(m)!
log.info("Starting hearbeat, with interval: {}", heartbeatInterval)!
setTimer(HeartbeatName, SendHeartbeat, heartInterval, repeat = true)!
akka-raft is a work in progress community project – it may change a lot
163. Raft Heartbeat using Akka
sendHeartbeat(m)!
log.info("Starting hearbeat, with interval: {}", heartbeatInterval)!
setTimer(HeartbeatName, SendHeartbeat, heartInterval, repeat = true)!
val leaderBehaviour = {!
// ...!
case Event(SendHeartbeat, m: LeaderMeta) =>!
sendHeartbeat(m)!
stay()!
akka-raft is a work in progress community project – it may change a lot
}
164. Akka-Raft in User-Land //alpha!!!
class WordConcatRaftActor extends RaftActor {!
!
type Command = Cmnd!
!
var words = Vector[String]()!
!
/** Applied when command committed by Raft consensus */!
def apply = {!
case AppendWord(word) =>!
words = words :+ word!
word!
!
case GetWords =>!
log.info("Replying with {}", words.toList)!
words.toList!
}!
}!
akka-raft is a work in progress community project – it may change a lot
173. Links
1. Excellent Paxos lecture by Diego Ongaro
https://www.youtube.com/watch?v=JEpsBg0AO6o
2. Fallacies, actual paper: http://www.rgoarchitects.com/Files/fallacies.pdf
3. Diego Ongaro & John Ouserhout – In search of an understandable consensus protocol
4. http://macs.citadel.edu/rudolphg/csci604/ImpossibilityofConsensus.pdf
Peter Deutsch “The Eight Fallacies of Distributed Computing”
https://blogs.oracle.com/jag/resource/Fallacies.html
174. Images / drawings
1. Paxos Island Photo – Luigi Piazzi (CC license) https://www.flickr.com/photos/photolupi/
3686769346/in/photolist-6BME5J-orKHL2-58qmez-58uz7s-7bRwTj-7bRvHY-6DdRC2-
fBqFFU-35KTg7-8vbe23-bsBGL7-58qq6z-58uAjG-8vbeCd-d1Sqqw-d1Smsj-d1Sqi5-
d1SoMA-d1SmBE-d1SpVo-d1Sk2U-d1SoBQ-d1SoXu-d1SoqN-d1Spqu-d1Sq4w-d1SpLU-d1SKDG-
d1Skcu-d1Sp8f-d1Sqaq-d1SpCw-75YaVN-d1SLs1-d1SK15-d1SJiC-d1Suiu-d1SKtS-d1SjQS-
d1StyU-d1SKi1-d1SxGS-d1Sm6j-d1Sxdh-d1SKMN-d1SxAq-d1SwgC-d1Smgj-d1SvhJ-
d1SjC7
2. Drawings – myself (use-them-at-will-unless-mocking-my-horrible-drawing-skills-license)
Peter Deutsch “The Eight Fallacies of Distributed Computing”
https://blogs.oracle.com/jag/resource/Fallacies.html