Implemen'ng	
  a	
  	
  
Distributed	
  Hash	
  Table	
  
with	
  Scala	
  and	
  Akka	
  
Tristan	
  Penman	
  
@tristanp...
This	
  Talk	
  in	
  a	
  nutshell	
  
1.  An	
  intro	
  to	
  DHTs,	
  the	
  Chord	
  protocol	
  and	
  its	
  
suppo...
But	
  first…	
  an	
  Akka	
  refresher	
  
•  Framework	
  for	
  building	
  concurrent,	
  
distributed,	
  and	
  resi...
An	
  intro	
  to	
  DHTs,	
  the	
  Chord	
  
protocol	
  and	
  its	
  suppor'ng	
  
algorithms	
  
Hash	
  Tables	
  in	
  one	
  slide	
  
•  A	
  hash	
  table	
  is	
  a	
  data	
  structure	
  
used	
  to	
  implement...
Distributed	
  Hash	
  Tables	
  
•  What	
  do	
  you	
  do	
  when	
  you	
  can’t	
  store	
  all	
  of	
  your	
  data...
The	
  Chord	
  Protocol	
  
•  Chord	
  is	
  a	
  protocol	
  and	
  set	
  of	
  
algorithms	
  for	
  implemen'ng	
  a...
Applica'on	
  Layer	
  
•  “Does	
  not	
  specify	
  applica'on	
  layer	
  behavior”	
  
– Does	
  not	
  prescribe	
  r...
Visualizing	
  Chord	
  
•  Instead	
  of	
  an	
  array,	
  we	
  have	
  a	
  
key-­‐space	
  which	
  can	
  be	
  visu...
Consistent	
  Hashing	
  
•  Chord	
  assigns	
  responsibility	
  for	
  segments	
  of	
  the	
  ring	
  to	
  
individu...
Adding	
  a	
  node	
  
Diagram	
  from:	
  hIps://www.cs.rutgers.edu/~pxk/417/notes/23-­‐lookup.html	
  
Node	
  6	
  has...
Lookup	
  and	
  Insert	
  Opera'ons	
  
•  Lookup	
  and	
  insert	
  opera'ons	
  are	
  based	
  on	
  key	
  
ownershi...
Network	
  Stabiliza'on	
  
•  Each	
  node	
  needs	
  to	
  maintain	
  
pointers	
  to	
  other	
  nodes	
  
–  Success...
Visualizing	
  Stabiliza'on	
  
•  Node	
  3	
  joins	
  network,	
  with	
  node	
  10	
  as	
  its	
  ini'al	
  successo...
Chord	
  algorithms	
  
•  A	
  Chord	
  network	
  can	
  be	
  thought	
  of	
  as	
  a	
  dynamic	
  distributed	
  
da...
A	
  quick	
  demo	
  
Code	
  available	
  at:	
  
hIps://github.com/tristanpenman/chordial	
  
Modeling	
  Chord	
  using	
  Actors	
  
(while	
  following	
  Akka	
  best	
  prac'ces)	
  
Actor	
  Model	
  
•  Computa'ons	
  defined	
  in	
  terms	
  of	
  individual	
  
components	
  that	
  respond	
  to	
  ...
What	
  (who)	
  are	
  our	
  actors?	
  
Node	
  1	
  
Node	
  2	
  
Node	
  3	
  
Node	
  4	
  
•  Nodes	
  are	
  an	
...
Decomposi'on	
  via	
  Best	
  Prac'ces	
  
•  By	
  iden'fying	
  some	
  Best	
  Prac'ces,	
  we	
  can	
  
take	
  a	
 ...
Case	
  study:	
  Stabiliza'on	
  
•  Chord	
  requires	
  that	
  a	
  node	
  should	
  periodically	
  
perform	
  a	
 ...
Stabiliza'on…	
  The	
  Wrong	
  Way	
  
class Node(val initialSuccessor: ActorRef) extends Actor {
var successor: ActorRe...
•  Actor	
  state	
  should	
  only	
  ever	
  evolve	
  in	
  response	
  
to	
  messages	
  received	
  from	
  the	
  o...
class MyActor extends Actor {
var counter = 0
override def receive: Receive = {
case IncrementCounter() =>
counter += 1
}
...
•  Actor	
  state	
  should	
  only	
  ever	
  be	
  mutated	
  with	
  a	
  
call	
  to	
  `context.become`	
  
– Using	
...
class MyActor extends Actor {
def activeSet(isInSet: Set[String]): Receive = {
case Add(key) =>
context.become(activeSet(i...
•  Actor	
  state	
  should	
  not	
  be	
  allowed	
  to	
  leak	
  into	
  
asynchronous	
  closures	
  
– A	
  Closure	...
class MyActor extends Actor {
def withConfig(config: String): Receive = ???
override def receive: Receive = {
case Initial...
Stabiliza'on…	
  Improved	
  
class Node(val initialSuccessor: ActorRef) extends Actor {
def doStabilization: Future[Actor...
Best	
  Prac'ces	
  in	
  Summary	
  
•  Here	
  are	
  the	
  three	
  Best	
  Prac'ces	
  that	
  we’ll	
  come	
  
back...
How	
  can	
  we	
  decompose	
  this	
  actor?	
  
•  Li:	
  the	
  stabiliza(on	
  algorithm	
  into	
  its	
  own	
  ac...
Timing	
  Logic	
  
•  We	
  can	
  also	
  li:	
  the	
  (ming	
  logic	
  into	
  its	
  own	
  actor	
  
•  Once	
  aga...
Our	
  addi'onal	
  actors	
  
Node	
  1	
  
Node	
  2	
  
Node	
  3	
  
Node	
  4	
  
Node	
  
Stabiliza'on	
  
Algorithm...
Division	
  of	
  responsibili'es	
  
•  Node	
  handles	
  requests	
  
•  Node	
  stores	
  network	
  pointers:	
  
–  ...
Akka	
  paIerns	
  
(Ask	
  and	
  Pipe)	
  
Stabiliza'on	
  constraints	
  
•  Only	
  one	
  stabiliza'on	
  request	
  should	
  be	
  in	
  
progress	
  at	
  any	...
Ask	
  paIern	
  
•  Ask	
  paIern	
  (import	
  akka.paIern.ask)	
  
–  Ask	
  (?)	
  instead	
  of	
  tell	
  (!)	
  
– ...
Ask	
  paIern	
  example	
  
val nodeIdFuture = nodeRef.ask(GetId())(requestTimeout)
.mapTo[GetIdResponse]
.map {
case Get...
Pipe	
  paIern	
  
•  Pipe	
  paIern	
  (import	
  akka.paIern.pipe)	
  
–  Complements	
  the	
  Ask	
  paIern	
  by	
  a...
Combining	
  Ask	
  and	
  Pipe	
  paIerns	
  
•  Requests	
  that	
  depend	
  on	
  the	
  output	
  of	
  an	
  
asynch...
Stabiliza'on	
  Using	
  Ask	
  and	
  Pipe	
  
class Node(val initialSuccessor: ActorRef) extends Actor {
def running(nod...
Bonus	
  'p:	
  Use	
  Sealed	
  Traits	
  
•  Model	
  your	
  message	
  types	
  using	
  Sealed	
  Traits	
  
–  Allow...
An	
  interes'ng	
  edge	
  case	
  
•  Allowing	
  concurrent	
  stabiliza'on	
  requests	
  could	
  lead	
  to	
  sub-­...
Possible	
  solu'on	
  (before	
  Join)	
  
Node	
  
(Request	
  Logic)	
  
Stabiliza'on	
  
Algorithm	
  	
  
Applica'on	...
Possible	
  solu'on	
  (aDer	
  Join)	
  
Stabiliza'on	
  
Algorithm	
  
Pointers	
  
(Data	
  Model)	
  
Graveyard	
  
• ...
Scala	
  resources	
  
•  Useful	
  resources	
  for	
  Scala	
  and	
  Akka:	
  
–  Principles	
  of	
  Reac:ve	
  Progra...
Papers	
  
•  “Chord:	
  A	
  Scalable	
  Peer-­‐to-­‐peer	
  Lookup	
  Protocol	
  for	
  Internet	
  	
  
Applica'ons”	
...
Libraries	
  used	
  
•  Backend:	
  
–  spray	
  (library	
  for	
  building	
  Akka-­‐based	
  web	
  services)	
  
hIps...
Thanks	
  for	
  listening	
  
	
  
Time	
  for	
  ques'ons!	
  
Email:	
  
tristan@tristanpenman.com	
  
	
  
Demo	
  cod...
Upcoming SlideShare
Loading in …5
×

Implementing a Distributed Hash Table with Scala and Akka

1,034 views

Published on

A talk summarising one developer's experience implementing a distributed hash table, or DHT, based on the Chord protocol. In particular, the talk will cover some Scala and Akka best practices, and look at how these practices influence the design of a non-trivial actor system.

Published in: Software
0 Comments
4 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
1,034
On SlideShare
0
From Embeds
0
Number of Embeds
12
Actions
Shares
0
Downloads
14
Comments
0
Likes
4
Embeds 0
No embeds

No notes for slide

Implementing a Distributed Hash Table with Scala and Akka

  1. 1. Implemen'ng  a     Distributed  Hash  Table   with  Scala  and  Akka   Tristan  Penman   @tristanpenman  
  2. 2. This  Talk  in  a  nutshell   1.  An  intro  to  DHTs,  the  Chord  protocol  and  its   suppor'ng  algorithms   2.  Demo  applica'on   3.  Modeling  the  Chord  protocol  using  actors  (while   following  Akka  best  prac'ces)   4.  Akka  paIerns  (Ask  and  Pipe)   5.  Closing  remarks  and  useful  resources  
  3. 3. But  first…  an  Akka  refresher   •  Framework  for  building  concurrent,   distributed,  and  resilient  message-­‐driven   applica'ons   – Based  on  asynchronous  message  passing   •  Emphasises  actor-­‐based  concurrency   – Model  individual  components  as  ‘Actors’   – Allows  us  to  build  an  applica'on  from  individual   components  that  respond  to  a  set  of  well-­‐ formaIed  messages  
  4. 4. An  intro  to  DHTs,  the  Chord   protocol  and  its  suppor'ng   algorithms  
  5. 5. Hash  Tables  in  one  slide   •  A  hash  table  is  a  data  structure   used  to  implement  an   associa've  array   •  Lookup  and  inser'on   opera'ons  run  in  constant-­‐'me   •  Each  element  of  the  array  is  a   bucket  that  contains  one  or   more  keys   –  Think  of  a  bucket  as  owning  a  non-­‐ con'guous  subset  of  the  key-­‐ space   Visual  representa'on  of   a  hash  table   Diagram  from:  hIp://math.hws.edu/eck/cs124/javanotes6/c10/s3.html  
  6. 6. Distributed  Hash  Tables   •  What  do  you  do  when  you  can’t  store  all  of  your  data  on  one   node?  (Or  don’t  want  to)   –  Spread  the  data  across  mul'ple  nodes,  with  each  node  taking   responsibility  for  a  por'on  of  the  key-­‐space   –  Nodes  ==  buckets   •  First  problem:   –  How  do  we  figure  out  which  node  is  responsible  for  a  key?   •  Second  problem:   –  How  do  we  handle  changes  to  the  network  topology?   –  Nodes  can  join  or  leave  the  network  at  any  'me  
  7. 7. The  Chord  Protocol   •  Chord  is  a  protocol  and  set  of   algorithms  for  implemen'ng  a   Distributed  Hash  Table   •  Key  features  are:   –  Lookup  'me  is  logarithmic  in  the   number  of  network  nodes   –  Asynchronous  network  stabiliza'on   protocol   –  Consistent  Hashing  minimizes   disrup'ons  when  nodes  join  or   leave  the  network   Final  paper  (published  in  Transac'ons  on  Networking):  hIps://pdos.csail.mit.edu/papers/ton:chord/paper-­‐ton.pdf  
  8. 8. Applica'on  Layer   •  “Does  not  specify  applica'on  layer  behavior”   – Does  not  prescribe  replica'on  techniques   – Same  applies  to  load  balancing  of  requests  coming   into  the  network   – Redistribu'on  of  data  associated  with  keys  when   nodes  leave  or  join  the  network  is  the   responsibility  of  the  applica'on  
  9. 9. Visualizing  Chord   •  Instead  of  an  array,  we  have  a   key-­‐space  which  can  be  visualized   as  a  ring   •  A  hash  func'on  is  used  to  map   keys  onto  loca'ons  on  the  ring   •  Nodes  are  also  mapped  to   loca'ons  on  the  ring   –  Typically  determined  by  applying  a   hash  func'on  to  their  IP  address  and   port  number   Empty  circles  represent  dis'nct   loca'ons  on  the  ring.  Blue-­‐filled   circles  indicate  that  nodes  exist   at  those  loca'ons.   Diagram  from:  hIps://www.cs.rutgers.edu/~pxk/417/notes/23-­‐lookup.html  
  10. 10. Consistent  Hashing   •  Chord  assigns  responsibility  for  segments  of  the  ring  to   individual  nodes   –  This  scheme  is  called  Consistent  Hashing   –  Allows  nodes  to  be  added  or  removed  from  the  network  while   minimizing  the  number  of  keys  that  will  need  to  be  reassigned   •  We  can  figure  out  which  node  owns  a  given  key  by  applying   the  hash  func'on,  then  choosing  the  node  whose  loca'on  on   the  ring  is  equal  to  or  greater  than  that  of  the  key   –  This  node  is  the  ‘successor’  of  the  key.  
  11. 11. Adding  a  node   Diagram  from:  hIps://www.cs.rutgers.edu/~pxk/417/notes/23-­‐lookup.html   Node  6  has   been  added  to   the  network  
  12. 12. Lookup  and  Insert  Opera'ons   •  Lookup  and  insert  opera'ons  are  based  on  key   ownership   –  When  we  want  to  find  a  key,  we  use  the  hash  func'on   to  find  its  loca'on  on  the  ring   –  Given  the  loca'on  of  a  key  on  the  ring,  Chord  allows   us  to  efficiently  iden'fy  its  successor   –  For  lookups,  we  ask  the  successor  whether  it  knows   about  the  key  that  we’re  interested  in   •  Applica'on-­‐layer  is  responsible  for  further  logic   –  For  insert  opera'ons,  we  tell  the  successor  to  store   the  given  key  
  13. 13. Network  Stabiliza'on   •  Each  node  needs  to  maintain   pointers  to  other  nodes   –  Successor(s)   –  Predecessor   –  Finger  table   •  Finger  table  is  a  list  of  nodes  at   increasing  distances  from  the   current  node   –  E.g.  n,  n+1,  n+2,  n+4,  …   –  Allows  for  shortcuts  across   segments  of  the  network,  hence  the   name  Chord   Finger  tables  allow  lookup   opera'ons  to  take  shortcuts  across   the  key-­‐space   Diagram  from:  hIps://en.wikipedia.org/wiki/Chord_(peer-­‐to-­‐peer)  
  14. 14. Visualizing  Stabiliza'on   •  Node  3  joins  network,  with  node  10  as  its  ini'al  successor   •  Node  3  begins  stabiliza'on;  asks  node  10  for  its  predecessor   •  Node  10  tells  node  3  that  its  predecessor  is  node  8   •  Node  8  is  closer  to  node  3,  and  becomes  its  new  successor   •  Node  3  no'fies  node  8  of  the  change,  so  node  3  updates  its  predecessor  pointer   Diagram  adapted  from:  hIps://www.cs.rutgers.edu/~pxk/417/notes/23-­‐lookup.html  
  15. 15. Chord  algorithms   •  A  Chord  network  can  be  thought  of  as  a  dynamic  distributed   data  structure   •  The  Chord  protocol  defines  eight  algorithms  that  are  used  to   navigate  and  maintain  this  data  structure:   –  CheckPredecessor   –  ClosestPrecedingNode   –  FindPredecessor   –  FindSuccessor   –  FixFingers   –  Join   –  No'fy   –  Stabilize   We’re  going  to  focus     on  Stabiliza(on  
  16. 16. A  quick  demo   Code  available  at:   hIps://github.com/tristanpenman/chordial  
  17. 17. Modeling  Chord  using  Actors   (while  following  Akka  best  prac'ces)  
  18. 18. Actor  Model   •  Computa'ons  defined  in  terms  of  individual   components  that  respond  to  a  set  of  well-­‐formaIed   messages   •  Group  of  actors  is  an  Actor  System   •  Message  Passing  is  asynchronous   •  But  nodes  process  messages  sequen'ally  
  19. 19. What  (who)  are  our  actors?   Node  1   Node  2   Node  3   Node  4   •  Nodes  are  an  obvious   star'ng  place…   –  Stores  network  pointers   –  Supports  message  nine   types,  along  with  the   appropriate  response   messages   –  Timing  logic  for   stabiliza'on…   –  This  is  star(ng  to  sound   really  complicated!  
  20. 20. Decomposi'on  via  Best  Prac'ces   •  By  iden'fying  some  Best  Prac'ces,  we  can   take  a  more  principled  approach  to   decomposing  our  Actor  System   •  We  want  to  preserve  three  key  proper'es:   – Determinism   – Immutability  and  referen'al  transparency   – Thread-­‐safety  
  21. 21. Case  study:  Stabiliza'on   •  Chord  requires  that  a  node  should  periodically   perform  a  stabiliza'on  opera'on   –  Stabiliza'on  ensures  that  the  node’s  successor  is  s'll  the   next  closest  node  on  the  ring   –  If  a  closer  node  is  found,  then  the  successor  pointer   needs  to  be  updated  (involves  a  state  change)   •  Let’s  look  at  how  we  might  implement  periodic   stabiliza'on  using  Scala  and  Akka…   –  Useful  exercise  to  explore  some  Akka  best  prac'ces  
  22. 22. Stabiliza'on…  The  Wrong  Way   class Node(val initialSuccessor: ActorRef) extends Actor { var successor: ActorRef = initialSuccessor def doStabilization: Future[ActorRef] = ??? override def receive: Receive = { case Stabilize() => val newSuccessorFuture = doStabilization() newSuccessorFuture.onSuccess { newSuccessor => successor = newSuccessor } } context.system.scheduler.schedule(3000.milliseconds, 3000.milliseconds, self, Stabilize()) }
  23. 23. •  Actor  state  should  only  ever  evolve  in  response   to  messages  received  from  the  outside   – Internal  scheduling  makes  an  actor’s  state  non-­‐ determinis'c,  which  is  par'cularly  bad  for  tes'ng   – Scheduling  should  take  place  outside  the  actor   class MyActor extends Actor { var counter = 0 override def receive: Receive = { case IncrementCounter() => counter += 1 } context.system.scheduler.schedule(2.seconds, 3.seconds, self, IncrementCounter()) }
  24. 24. class MyActor extends Actor { var counter = 0 override def receive: Receive = { case IncrementCounter() => counter += 1 } } object MyActor { case class IncrementCounter() } class MyApp extends App { val myActor = context.actorOf(Props(classOf[MyActor])) system.scheduler.schedule(2.seconds, 3.seconds, myActor, IncrementCounter()) }
  25. 25. •  Actor  state  should  only  ever  be  mutated  with  a   call  to  `context.become`   – Using  vars  (or  vals  for  mutable  objects)  allows   unintended  states  to  be  introduced   – Prefer  immutability  and  referen'al  transparency   class MyActor extends Actor { val isInSet = mutable.Set.empty[String] override def receive: Receive = { case AddToSet(key) => isInSet += key case Contains(key) => sender() ! isInSet(key) } }
  26. 26. class MyActor extends Actor { def activeSet(isInSet: Set[String]): Receive = { case Add(key) => context.become(activeSet(isInSet + key)) case Contains(key) => sender() ! isInSet(key) } override def receive: Receive = active(Set.empty) }
  27. 27. •  Actor  state  should  not  be  allowed  to  leak  into   asynchronous  closures   – A  Closure  may  be  executed  on  another  thread   – Akka’s  Context  class  is  not  thread-­‐safe,  which   means  no  more  calls  to  ‘context.become’:   class MyActor extends Actor { def withConfig(config: String): Receive = ??? override def receive: Receive = { case Initialise() => val myFuture = loadConfig() myFuture.onSuccess { config => context.become(withConfig(config)) } } }
  28. 28. class MyActor extends Actor { def withConfig(config: String): Receive = ??? override def receive: Receive = { case Initialise() => val myFuture = loadConfig() myFuture.onSuccess { config => self ! ConfigLoaded(config) } case ConfigLoaded(config) => context.become(withConfig(config)) } }
  29. 29. Stabiliza'on…  Improved   class Node(val initialSuccessor: ActorRef) extends Actor { def doStabilization: Future[ActorRef] = ??? def active(successor: ActorRef): Receive = { case Stabilize() => val newSuccessorFuture = doStabilization() newSuccessorFuture.onSuccess { newSuccessor => self ! Stabilized(newSuccessor) } case Stabilized(newSuccessor) => context.become(active(newSuccessor)) } override def receive: Receive = active(initialSuccessor) }
  30. 30. Best  Prac'ces  in  Summary   •  Here  are  the  three  Best  Prac'ces  that  we’ll  come   back  to  while  designing  our  Actor  System:   1.  Actor  state  should  only  ever  evolve  in  response  to   messages  received  from  the  outside   2.  Actor  state  should  only  ever  be  mutated  with  a  call  to   `context.become`   3.  Actor  state  should  not  be  allowed  to  leak  into   asynchronous  closures   Based  on  best  prac'ces  documented  at:  hIps://github.com/alexandru/scala-­‐best-­‐prac'ces  
  31. 31. How  can  we  decompose  this  actor?   •  Li:  the  stabiliza(on  algorithm  into  its  own  actor   •  Allows  us  to  achieve  our  three  best  prac'ces   –  “Actor  state  should  only  ever  evolve  in  response  to   messages  received  from  the  outside”  (determinism)   –  “Actor  state  should  only  ever  be  mutated  with  a  call  to   context.become”  (immutability  and  referen'al   transparency)   –  “Actor  state  should  not  be  allowed  to  leak  into   asynchronous  closures”  (thread-­‐safety)  
  32. 32. Timing  Logic   •  We  can  also  li:  the  (ming  logic  into  its  own  actor   •  Once  again,  achieves  our  three  best  prac'ces   –  In  par'cular,  ensures  that  the  Node  state  only  evolves  in   response  to  messages  from  the  outside!  
  33. 33. Our  addi'onal  actors   Node  1   Node  2   Node  3   Node  4   Node   Stabiliza'on   Algorithm   Interface   Timing   Logic   Applica'on  
  34. 34. Division  of  responsibili'es   •  Node  handles  requests   •  Node  stores  network  pointers:   –  Successor   –  Predecessor  (op'onal)   –  Finger  table  (see  Chord  paper)   •  TimingLogic  triggers     stabiliza'on   •  Stabiliza'onAlgorithm     runs  asynchronous       Node   Stabiliza'on   Algorithm   Interface   Timing   Logic   Applica'on  
  35. 35. Akka  paIerns   (Ask  and  Pipe)  
  36. 36. Stabiliza'on  constraints   •  Only  one  stabiliza'on  request  should  be  in   progress  at  any  given  'me   •  Stabiliza'on  should  fail  if  the  algorithm   exceeds  a  given  'meout   •  When  stabiliza'on  finishes,  a  no'fica'on   should  be  sent  to  the  TimingLogic  actor   – so  that  it  can  adjust  the  frequency  of  stabiliza'on   requests  based  on  'me-­‐to-­‐complete  or  failure   rate  
  37. 37. Ask  paIern   •  Ask  paIern  (import  akka.paIern.ask)   –  Ask  (?)  instead  of  tell  (!)   –  Returns  a  Future  that  will  complete  with  the  first  response   from  the  target  actor   –  Takes  a  Timeout  value,  which  specifies  how  long  to  wait   un'l  the  Future  should  fail   –  This  can  be  nicer  than  setReceiveTimeout     •  “Stabiliza:on  should  fail  if  the  algorithm  exceeds  a   given  :meout”  
  38. 38. Ask  paIern  example   val nodeIdFuture = nodeRef.ask(GetId())(requestTimeout) .mapTo[GetIdResponse] .map { case GetIdOk(nodeId) => Some(NodeInfo(nodeId, nodeRef)) } .recover { case ex => log.error(s“GetId failed: ${ex.getMessage}”) None } •  Asking  a  node  for  its  ID:  
  39. 39. Pipe  paIern   •  Pipe  paIern  (import  akka.paIern.pipe)   –  Complements  the  Ask  paIern  by  allowing  the  result  of  a   Future  to  be  piped  to  an  actor   –  Augments  Futures  with  the  pipeTo  method   •  onSuccess  -­‐>  sends  result  to  actor   •  onFailure  -­‐>  sends  excep'on  to  actor,  as  an   akka.actor.Status.Failure   •  “When  stabiliza:on  finishes,  a  no:fica:on   should  be  sent  to  the  TimingLogic  actor”    
  40. 40. Combining  Ask  and  Pipe  paIerns   •  Requests  that  depend  on  the  output  of  an   asynchronous  algorithm  will  create  (or  reuse)  an   algorithm  actor   –  Using  the  Ask  paIern  gives  us  a  Future  that  will  expire   auer  a  fixed  amount  of  'me   –  Transform  and  ‘pipe’  result  to  client  that  originally  made   the  request   –  Allows  async  request  handling  to  take  place  outside  of  the   main  thread  
  41. 41. Stabiliza'on  Using  Ask  and  Pipe   class Node(val initialSuccessor: ActorRef) extends Actor { def running(nodeRef: ActorRef, algorithm: ActorRef): Receive = { case Stabilize() => algorithm.ask(StabilizationStart(nodeRef))(timeout) .mapTo[StabilizationStartResponse] .map { case StabilizationComplete() => StabilizeOk() case StabilizationAlreadyRunning => StabilizeInProgress() case StabilizationFailed(m) => StabilizeFailed(m) } .recover { case ex => StabilizeFailed(ex.getMessage) } .pipeTo(sender()) } override def receive: Receive = running( context.actorOf(Props(classOf[Node], initialSuccessor)), context.actorOf(Props(classOf[StabilizationAlgorithm]))) }
  42. 42. Bonus  'p:  Use  Sealed  Traits   •  Model  your  message  types  using  Sealed  Traits   –  Allows  the  Scala  compiler  to  validate  the  exhaus'veness  of   paIern  matching   algorithm.ask(StabilizationStart(nodeRef))(timeout) .mapTo[StabilizationStartResponse] .map { case StabilizationComplete() => StabilizeOk() case StabilizationAlreadyRunning => StabilizeInProgress() case StabilizationFailed(m) => StabilizeFailed(m) } .recover { case ex => StabilizeFailed(ex.getMessage) } .pipeTo(sender())
  43. 43. An  interes'ng  edge  case   •  Allowing  concurrent  stabiliza'on  requests  could  lead  to  sub-­‐ op'mal  network  behaviour   –  Maintain  one  instance  of  the  Stabiliza'onAlgorithm  actor   –  While  running,  it  will  respond  with  an  AlreadyInProgress  message  for   any  aIempt  to  restart  it   •  When  joining  a  new  network,  algorithm  actors  are  terminated   and  replaced  with  new  actors.  However,  messages  from  old   actors  may  s'll  be  queued!   •  So  once  we  join  a  new  network,  we  want  need  a  way  to   ignore  any  queued  messages  that  may  alter  network  pointers   –  Accep'ng  these  messages  could  lead  to  the  Node  being  split  across   two  networks  
  44. 44. Possible  solu'on  (before  Join)   Node   (Request  Logic)   Stabiliza'on   Algorithm     Applica'on   Timing   Logic   Pointers   (Data  Model)   •  Join  request  causes  Pointers   and  Stabiliza'onAlgorithm   actors  to  be  stopped   •  context.stop  achieves  this   using  a  message  send,  so   other  messages  may  be  in   their  queues   •  New  Pointers  and   Stabiliza'onAlgorithm   actors  are  created   •  Old  actors  are  effec'vely   detached  and  cannot  alter   state  of  Node   Not  sure  that  this  is  the  best     approach…  
  45. 45. Possible  solu'on  (aDer  Join)   Stabiliza'on   Algorithm   Pointers   (Data  Model)   Graveyard   •  Node  becomes  a  proxy  /   controller  for  Pointers  actor   –  Maybe  even  the  ‘Interface’?   –  Pointers  actor  becomes  model   •  Stabiliza'onAlgorithm  sends   update  messages  to   Pointers  actor   Node   (Request  Logic)   Stabiliza'on   Algorithm     Applica'on   Timing   Logic   Pointers   (Data  Model)   Not  sure  that  this  is  the  best     approach…  
  46. 46. Scala  resources   •  Useful  resources  for  Scala  and  Akka:   –  Principles  of  Reac:ve  Programming  course  on  Coursera   hIps://www.coursera.org/course/reac've   –  Func'onal  Programming  in  Scala   (book  by  Chiusano  and  Runar)   –  Scala  Best  Prac'ces  (includes  some  Akka  best  prac'ces)   hIps://github.com/alexandru/scala-­‐best-­‐prac'ces    
  47. 47. Papers   •  “Chord:  A  Scalable  Peer-­‐to-­‐peer  Lookup  Protocol  for  Internet     Applica'ons”  (IEEE  Transac'ons  on  Networking  version)   hIps://pdos.csail.mit.edu/papers/ton:chord/paper-­‐ton.pdf   •  “Consistent  Hashing  and  Random  Trees-­‐  Distributed  Caching   Protocols  for  Relieving  Hot  Spots  on  the  World  Wide  Web”,   hIp://www.ccs.neu.edu/home/cbw/4700/papers/akamai.pdf  
  48. 48. Libraries  used   •  Backend:   –  spray  (library  for  building  Akka-­‐based  web  services)   hIps://github.com/spray/spray   –  spray-­‐json  (JSON  de-­‐/serialisa'on,  spun  off  from  spray)   hIps://github.com/spray/spray-­‐json   –  spray-­‐websocket  (Stream  data  over  HTTP  -­‐  this  used  to  be  really  hard!)   hIps://github.com/wandoulabs/spray-­‐websocket   –  scalastyle  (detect  code  smells,  formawng  errors,  etc)   hIp://www.scalastyle.org   •  Frontend:   –  d3.js  (bring  data  to  life  using  HTML,  SVG  and  CSS)   hIps://github.com/mbostock/d3  
  49. 49. Thanks  for  listening     Time  for  ques'ons!   Email:   tristan@tristanpenman.com     Demo  code  available  at   hIps://github.com/tristanpenman/chordial  

×