Scalability in Scala and Java Nadav Wiener
About me • Nadav Wiener (@nadavwr) • Senior Consultant & Architect @ AlphaCSP • Following Scala since 2007
Agenda Akka The problem with locks Actors & message passing High availability & remoting STM & Transactors
What is Akka? • A actor-based concurrency framework • Provides solutions for non blocking concurrency • Written in Scala, also works in Java • Open source (APL) • Now releasing 1.0 after 2 years in development • Lead developer and founder: Jonas Boner • JRockit, AspectWerkz, AspectJ, Terracotta
“Queen of Lapland” 5
You may also remember… 6
The Problem
Ignorance Is a Bliss If (account1.getBalance() > amount) { account1.withdraw(amount) account2.deposit(amount) } Sewing on a button © Dvortygirl, 17 February 2008. 8
Not an option if you plan on having business
So You Use Threads Coloured Thread © Philippa Willitts, 24 April 2008. 10
But Without Synchronization You Get Unpredictable Behavior 11
Lock based concurrency requires us to constantly second guess our code 12
“The definition of insanity is doing the same thing over and over again and expecting different results. “ – Albert Einstein 13
People Are Bad At Thinking Parallel 14
So you synchronize With locks? 15
locking 16
Blocking 17
Lock too little Lock too much 18
Lock recklessly? 19
Using locks recklessly 20
Must Think Globally: Lock ordering Contention Everybody’s code 22
Knowing shared-state concurrency != confidence 23
Keep those cores busy! Cores aren’t getting faster Default thread stack size on AMD64 = 1MB! Context switching hurts throughput
25
Actors
Shakespeare Programming Language
Excerpt from "Hello World" in SPL Romeo, a young man with a remarkable patience. Juliet, a likewise young woman of remarkable grace. Scene II: The praising of Juliet. [Enter Juliet] Romeo: Thou art as sweet as the sum of the sum of Hamlet and his horse and his black cat! Speak thy mind! [Exit Juliet]
Actors have nothing to do with the Shakespeare Programming Language
Actors • Each actor has a message queue • Actors accept and choose what to do with messages • Lightweight & asynchronous
Actors • Each actor has a message queue • Actors accept and choose what to do with messages • Lightweight & asynchronous
Actors • Each actor has a message queue • Actors accept and choose what to do with messages • Lightweight & asynchronous
Actors • Each actor has a message queue • Actors accept and choose what to do with messages • Lightweight & asynchronous
Actors • Each actor has a message queue • Actors accept and choose what to do with messages • Lightweight & asynchronous
Actors • Each actor has a message queue • Actors accept and choose what to do with messages • Lightweight & asynchronous
Actors ~4 threads 4 cores • Actors tend to remain bound to a single thread • Actors rarely block, thread can remain active for a Actors long duration • Minimizes context switches – throughput X millions • Akka actors occupy 650 bytes
Benchmark • Several actor implementations for Scala – Akka is considered the fastest
Akka Actors // Java // Scala public class GreetingActor extends class GreetingActor extends Actor { UntypedActor { private var counter = 0 private int counter = 0; def receive = { public void onReceive(Object message) throws Exception { case message => { counter++; counter += 1 // 1) Hello, Juliet // 1) Hello, Juliet log.info( log.info( counter + ") Hello, " + message); counter + ") Hello, " + message) } } } } }
Akka Actors // Java // Scala ActorRef Romeo = val greetingActor = actorOf(GreetingActor.class).start(); actorOf[GreetingActor].start greetingActor.sendOneWay("Juliet"); greetingActor ! "Juliet“ // 1) Hello, Juliet // 1) Hello, Juliet
Akka Actors • Once instantiated, actors can be retrieved by id or uuid • uuid - generated by runtime, immutable, unique • id - user assigned, by default that's the class name class Romeo extend GreetingActor { self.id = "Romeo" } actorOf[Romeo].start val romeo = actorsFor("Romeo").head romeo ! "Juliet"
Message Passing
Message Passing • Let's build a bank with one actor per account, We’ll be able to : • Check our balance • Deposit money • Withdraw money, but only if we have it (balance remains >= 0) • We'll start by defining immutable message types: case class CheckBalance() case class Deposit(amount: Int) case class Withdraw(amount: Int)
Message Passing class BankAccount(private var balance: Int = 0) extends Actor { def receive = { // … case CheckBalance => self.reply_?(balance) // … } }
Message Passing class BankAccount(private var balance: Int = 0) extends Actor { def receive = { // … case Deposit(amount) => balance += amount // … } }
Message Passing class BankAccount(private var balance: Int = 0) extends Actor { def receive = { // … case Withdraw(amount) => balance = (balance - amount) ensuring (_ >= 0) // … } }
Message Passing • Now let’s make a deposit: val account = actorOf[BankAccount].start account ! Deposit(100) • But how do we check the account balance?
Bang! Bang Bang! • actorRef ! message - fire and forget • actorRef !! message - send and block (optional timeout) for response • actorRef !!! message - send, reply using future ...Jones Boner promised to stop at "!!!" Java equivalents: • ! = sendOneWay • !! = sendRequestReply • !!! = sendRequestReplyFuture
Getting Account Balance val balance = (account !! CheckBalance) match { // do stuff with result } // or: val balanceFuture = account !!! CheckBalance // does not block // ... go do some other stuff ... later: val balance = balanceFuture.await.result match { // do stuff with result }
Fault Tolerance
Too Big to Fail 51
Jenga Architecture 52
The harder they fall 53
54
Self Healing, Graceful Recovery 55
Supervision Hierarchies Supervisors: Kind of actors Fault Handling Strategy: One for One or All for One Lifecycle: Permanent or Temporary
Fault Handling Strategy: One For One
One for One All for One, Permanent One for One, Chat Room, Temporary Permanent Romeo, Juliet, Permanent Permanent 59
One for One All for One, Permanent One for One, Chat Room, Temporary Permanent Romeo, Juliet, Permanent Permanent 60
One for One All for One, Permanent One for One, Chat Room, Temporary Permanent Romeo, Juliet, Permanent Permanent 61
One for One All for One, Permanent One for One, Chat Room, Temporary Permanent Romeo, Juliet, Permanent Permanent 62
One for One All for One, Permanent One for One, Chat Room, Temporary Permanent Romeo, Juliet, Permanent Permanent 63
Fault Handling Strategy: All For One
All for One All For One, Permanent One for One, Chat Room, Permanent Permanent Romeo, Juliet, Temporary Temporary 67
All for One All For One, Permanent One for One, Chat Room, Permanent Permanent Romeo, Juliet, Temporary Temporary 68
All for One All For One, Permanent One for One, Chat Room, Permanent Permanent Romeo, Juliet, Temporary Temporary 69
All for One All For One, Permanent One for One, Chat Room, Permanent Permanent Romeo, Juliet, Temporary Temporary 70
All for One All For One, Permanent One for One, Chat Room, Permanent Permanent Romeo, Juliet, Temporary Temporary 71
All for One All For One, Permanent One for One, Chat Room, Permanent Permanent Romeo, Juliet, Temporary Temporary 72
Supervision, Remoting & HA val supervisor = Supervisor(SupervisorConfig( AllForOneStrategy( List(classOf[Exception]), 3, 1000), List(Supervise(actorOf[ChatServer], Permanent), Supervise(actorOf[ChatServer], Permanent, RemoteAddess("host1", 9000)) ) ))
High Availability • You can’t have a highly available system on a single computer • Luckily Akka supports near-seamless remote actors
High Availability • Server managed remote actors: // on host1 RemoteNode.start("host1", 9000) // register an actor RemoteNode.register(“romeo", actorOf[GreetingActor]) // on host2 val romeo = RemoteClient.actorFor(“romeo", "host1", 9000) romero ! “juliet" • RemoteClient handles the connection lifecycle for us • Clients can also manage server actors, but enabling this might pose a security risk
Actor model – a life choice?
High scalability Assumes state travels Hgh availability along the message Fast flow Etc… Hostile towards shared state. Minds not easily rewired for this!
Brain Transplant
Software Transactional Memory
Rich Hickey (Clojure) Persistent Data Structures
Persistent Data Structures • Share common immutable structure and data • Copy-on-write semantics: val prices = TransactionalMap[String, Double] atomic { prices += ("hamburger" -> 20.0) } • When “modified”, minimal changes to structure are made to accommodate new data © Rich Hickey 2009
How many people seated in the audience?
If I started counting, by the time I finished… 1, 2, 3, 4, 5, …
…the room would be empty
Jonas Boner
Transactors class BankAccount extends Transactor { private val balanceRef = Ref(0) def atomically = { // ... case CheckBalance => self reply_? balance.get // ... } }
Transactors class BankAccount extends Transactor { private val balanceRef = Ref(0) def atomically = { // ... case Deposit(amount) => balance alter (_ + amount) // ... } }
Transactors class BankAccount extends Transactor { private val balanceRef = Ref(0) def atomically = { // ... case Withdraw(amount) => balance alter (_ - amount) ensuring (_.get >= 0) // ... } }
Transactors Performing a money transfer transactionally: val tx = Coordinated() val fromBalance = (from !! tx(CheckBalance())) match { balance: Int => balance } if (fromBalance >= 50) { from ! tx(Withdraw(50)) to ! tx(Deposit(50)) }
Coordinated Transactions class Bank extends Actor { private val accounts = TransactionalVector[BankAccount] def receive = { // ... case tx @ Coordinated(Join) => { tx atomic { accounts += self.sender.get } } // ... }
Coordinated Transactions class Bank extends Actor { private val accounts = TransactionalVector[BankAccount] def receive = { // ... case tx @ Coordinated(Sum) => { val futures = for (account <- accounts) yield account !!! tx(CheckBalance) val allTheMoney = futures map (_.await.result) sum self reply_? allTheMoney } // ... } …and then: println (myBank !! Coordinated(Sum))
Takeaways
Knowing shared-state concurrency != confidence 94
References
http://akkasource.org 96
http://scalablesolutions.se 97
http://alphacsp.com
Questions?
Typed Actors in Java • In Java we will usually avoid “untyped” actors, and use POJOs instead: interface BankAccount { Future<Integer> balance(); void deposit(int amount); void withdraw(int amount); } class BankAccountImpl extends TypedActor implements BankAccount { // Almost a regular POJO public Future<Integer> balance() { return future(balance); } // ... }

Akka -- Scalability in Scala and Java

  • 1.
    Scalability in Scalaand Java Nadav Wiener
  • 2.
    About me • Nadav Wiener (@nadavwr) • Senior Consultant & Architect @ AlphaCSP • Following Scala since 2007
  • 3.
    Agenda Akka The problem withlocks Actors & message passing High availability & remoting STM & Transactors
  • 4.
    What is Akka? • A actor-based concurrency framework • Provides solutions for non blocking concurrency • Written in Scala, also works in Java • Open source (APL) • Now releasing 1.0 after 2 years in development • Lead developer and founder: Jonas Boner • JRockit, AspectWerkz, AspectJ, Terracotta
  • 5.
  • 6.
    You may alsoremember… 6
  • 7.
  • 8.
    Ignorance Is aBliss If (account1.getBalance() > amount) { account1.withdraw(amount) account2.deposit(amount) } Sewing on a button © Dvortygirl, 17 February 2008. 8
  • 9.
    Not an option ifyou plan on having business
  • 10.
    So You UseThreads Coloured Thread © Philippa Willitts, 24 April 2008. 10
  • 11.
    But Without Synchronization YouGet Unpredictable Behavior 11
  • 12.
    Lock based concurrencyrequires us to constantly second guess our code 12
  • 13.
    “The definition ofinsanity is doing the same thing over and over again and expecting different results. “ – Albert Einstein 13
  • 14.
    People Are BadAt Thinking Parallel 14
  • 15.
    So you synchronize With locks? 15
  • 16.
  • 17.
  • 18.
    Lock too little Lock too much 18
  • 19.
  • 20.
  • 21.
    Must Think Globally: Lock ordering Contention Everybody’s code 22
  • 22.
  • 23.
    Keep those coresbusy! Cores aren’t getting faster Default thread stack size on AMD64 = 1MB! Context switching hurts throughput
  • 24.
  • 25.
  • 26.
  • 27.
    Excerpt from "HelloWorld" in SPL Romeo, a young man with a remarkable patience. Juliet, a likewise young woman of remarkable grace. Scene II: The praising of Juliet. [Enter Juliet] Romeo: Thou art as sweet as the sum of the sum of Hamlet and his horse and his black cat! Speak thy mind! [Exit Juliet]
  • 28.
    Actors have nothingto do with the Shakespeare Programming Language
  • 29.
    Actors • Each actor has a message queue • Actors accept and choose what to do with messages • Lightweight & asynchronous
  • 30.
    Actors • Each actor has a message queue • Actors accept and choose what to do with messages • Lightweight & asynchronous
  • 31.
    Actors • Each actor has a message queue • Actors accept and choose what to do with messages • Lightweight & asynchronous
  • 32.
    Actors • Each actor has a message queue • Actors accept and choose what to do with messages • Lightweight & asynchronous
  • 33.
    Actors • Each actor has a message queue • Actors accept and choose what to do with messages • Lightweight & asynchronous
  • 34.
    Actors • Each actor has a message queue • Actors accept and choose what to do with messages • Lightweight & asynchronous
  • 35.
    Actors ~4 threads 4 cores • Actors tend to remain bound to a single thread • Actors rarely block, thread can remain active for a Actors long duration • Minimizes context switches – throughput X millions • Akka actors occupy 650 bytes
  • 36.
    Benchmark • Several actor implementations for Scala – Akka is considered the fastest
  • 37.
    Akka Actors // Java // Scala public class GreetingActor extends class GreetingActor extends Actor { UntypedActor { private var counter = 0 private int counter = 0; def receive = { public void onReceive(Object message) throws Exception { case message => { counter++; counter += 1 // 1) Hello, Juliet // 1) Hello, Juliet log.info( log.info( counter + ") Hello, " + message); counter + ") Hello, " + message) } } } } }
  • 38.
    Akka Actors // Java // Scala ActorRef Romeo = val greetingActor = actorOf(GreetingActor.class).start(); actorOf[GreetingActor].start greetingActor.sendOneWay("Juliet"); greetingActor ! "Juliet“ // 1) Hello, Juliet // 1) Hello, Juliet
  • 39.
    Akka Actors • Once instantiated, actors can be retrieved by id or uuid • uuid - generated by runtime, immutable, unique • id - user assigned, by default that's the class name class Romeo extend GreetingActor { self.id = "Romeo" } actorOf[Romeo].start val romeo = actorsFor("Romeo").head romeo ! "Juliet"
  • 40.
  • 41.
    Message Passing • Let's build a bank with one actor per account, We’ll be able to : • Check our balance • Deposit money • Withdraw money, but only if we have it (balance remains >= 0) • We'll start by defining immutable message types: case class CheckBalance() case class Deposit(amount: Int) case class Withdraw(amount: Int)
  • 42.
    Message Passing class BankAccount(privatevar balance: Int = 0) extends Actor { def receive = { // … case CheckBalance => self.reply_?(balance) // … } }
  • 43.
    Message Passing class BankAccount(privatevar balance: Int = 0) extends Actor { def receive = { // … case Deposit(amount) => balance += amount // … } }
  • 44.
    Message Passing class BankAccount(privatevar balance: Int = 0) extends Actor { def receive = { // … case Withdraw(amount) => balance = (balance - amount) ensuring (_ >= 0) // … } }
  • 45.
    Message Passing • Now let’s make a deposit: val account = actorOf[BankAccount].start account ! Deposit(100) • But how do we check the account balance?
  • 46.
    Bang! Bang Bang! • actorRef ! message - fire and forget • actorRef !! message - send and block (optional timeout) for response • actorRef !!! message - send, reply using future ...Jones Boner promised to stop at "!!!" Java equivalents: • ! = sendOneWay • !! = sendRequestReply • !!! = sendRequestReplyFuture
  • 47.
    Getting Account Balance valbalance = (account !! CheckBalance) match { // do stuff with result } // or: val balanceFuture = account !!! CheckBalance // does not block // ... go do some other stuff ... later: val balance = balanceFuture.await.result match { // do stuff with result }
  • 48.
  • 49.
    Too Big toFail 51
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
    Supervision Hierarchies Supervisors: Kindof actors Fault Handling Strategy: One for One or All for One Lifecycle: Permanent or Temporary
  • 55.
  • 56.
    One for One All for One, Permanent One for One, Chat Room, Temporary Permanent Romeo, Juliet, Permanent Permanent 59
  • 57.
    One for One All for One, Permanent One for One, Chat Room, Temporary Permanent Romeo, Juliet, Permanent Permanent 60
  • 58.
    One for One All for One, Permanent One for One, Chat Room, Temporary Permanent Romeo, Juliet, Permanent Permanent 61
  • 59.
    One for One All for One, Permanent One for One, Chat Room, Temporary Permanent Romeo, Juliet, Permanent Permanent 62
  • 60.
    One for One All for One, Permanent One for One, Chat Room, Temporary Permanent Romeo, Juliet, Permanent Permanent 63
  • 61.
  • 62.
    All for One All For One, Permanent One for One, Chat Room, Permanent Permanent Romeo, Juliet, Temporary Temporary 67
  • 63.
    All for One All For One, Permanent One for One, Chat Room, Permanent Permanent Romeo, Juliet, Temporary Temporary 68
  • 64.
    All for One All For One, Permanent One for One, Chat Room, Permanent Permanent Romeo, Juliet, Temporary Temporary 69
  • 65.
    All for One All For One, Permanent One for One, Chat Room, Permanent Permanent Romeo, Juliet, Temporary Temporary 70
  • 66.
    All for One All For One, Permanent One for One, Chat Room, Permanent Permanent Romeo, Juliet, Temporary Temporary 71
  • 67.
    All for One All For One, Permanent One for One, Chat Room, Permanent Permanent Romeo, Juliet, Temporary Temporary 72
  • 68.
    Supervision, Remoting &HA val supervisor = Supervisor(SupervisorConfig( AllForOneStrategy( List(classOf[Exception]), 3, 1000), List(Supervise(actorOf[ChatServer], Permanent), Supervise(actorOf[ChatServer], Permanent, RemoteAddess("host1", 9000)) ) ))
  • 69.
    High Availability • You can’t have a highly available system on a single computer • Luckily Akka supports near-seamless remote actors
  • 70.
    High Availability • Server managed remote actors: // on host1 RemoteNode.start("host1", 9000) // register an actor RemoteNode.register(“romeo", actorOf[GreetingActor]) // on host2 val romeo = RemoteClient.actorFor(“romeo", "host1", 9000) romero ! “juliet" • RemoteClient handles the connection lifecycle for us • Clients can also manage server actors, but enabling this might pose a security risk
  • 71.
    Actor model –a life choice?
  • 72.
    High scalability Assumes state travels Hgh availability along the message Fast flow Etc… Hostile towards shared state. Minds not easily rewired for this!
  • 73.
  • 74.
  • 75.
    Rich Hickey (Clojure) Persistent Data Structures
  • 76.
    Persistent Data Structures • Share common immutable structure and data • Copy-on-write semantics: val prices = TransactionalMap[String, Double] atomic { prices += ("hamburger" -> 20.0) } • When “modified”, minimal changes to structure are made to accommodate new data © Rich Hickey 2009
  • 77.
    How many peopleseated in the audience?
  • 78.
    If I startedcounting, by the time I finished… 1, 2, 3, 4, 5, …
  • 79.
  • 80.
  • 81.
    Transactors class BankAccount extendsTransactor { private val balanceRef = Ref(0) def atomically = { // ... case CheckBalance => self reply_? balance.get // ... } }
  • 82.
    Transactors class BankAccount extendsTransactor { private val balanceRef = Ref(0) def atomically = { // ... case Deposit(amount) => balance alter (_ + amount) // ... } }
  • 83.
    Transactors class BankAccount extendsTransactor { private val balanceRef = Ref(0) def atomically = { // ... case Withdraw(amount) => balance alter (_ - amount) ensuring (_.get >= 0) // ... } }
  • 84.
    Transactors Performing a moneytransfer transactionally: val tx = Coordinated() val fromBalance = (from !! tx(CheckBalance())) match { balance: Int => balance } if (fromBalance >= 50) { from ! tx(Withdraw(50)) to ! tx(Deposit(50)) }
  • 85.
    Coordinated Transactions class Bankextends Actor { private val accounts = TransactionalVector[BankAccount] def receive = { // ... case tx @ Coordinated(Join) => { tx atomic { accounts += self.sender.get } } // ... }
  • 86.
    Coordinated Transactions class Bankextends Actor { private val accounts = TransactionalVector[BankAccount] def receive = { // ... case tx @ Coordinated(Sum) => { val futures = for (account <- accounts) yield account !!! tx(CheckBalance) val allTheMoney = futures map (_.await.result) sum self reply_? allTheMoney } // ... } …and then: println (myBank !! Coordinated(Sum))
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
    Typed Actors inJava • In Java we will usually avoid “untyped” actors, and use POJOs instead: interface BankAccount { Future<Integer> balance(); void deposit(int amount); void withdraw(int amount); } class BankAccountImpl extends TypedActor implements BankAccount { // Almost a regular POJO public Future<Integer> balance() { return future(balance); } // ... }