620 likes | 737 Views
How we Scale using Play , Akka , and Scala. Who are we?. Ryan Knight Consultant. Brian Pugh VP of Engineering. Lucidchart : Online diagramming solution. Lucidchart’s Story. … Lucidchart was in a precarious situation. Threat #1: Scaling.
E N D
Who are we? • Ryan Knight • Consultant • Brian Pugh • VP of Engineering
Threat #1: Scaling • Webservers scaled horizontally but required a large number of instances • DB scaled vertically which becomes prohibitively expensive
Threat #2: Performance Minimum and average response times too high
Threat #3: Availability Monolithic app: Any functional area of the application could take down the entire site 99.99999999999
4. Persistence layer that allowed us to control queries
7. Efficient development environment
What is Scala? Agile, with lightweight syntax Object-oriented Functional Safe and performant, with strong static typing
How does the Typesafe platform measure up to Lucidchart’s goals?
valroleIds = SQL("SELECT role_id FROM users_roles WHERE user_id={user_id}") .on("user_id" -> id) .as(get[Long]("role_id") *)
Immutable Person Class in Java public class Person { private final String name; private final intiq; public Person(String name, intiq) { this.name = name; this.iq = iq; } public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Person person = (Person) o; if (iq != person.iq) return false; if (name != null ? !name.equals(person.name) : person.name != null) return false; return true; } public inthashCode() { int result = name != null ? name.hashCode() : 0; result = 31 * result + iq; return result; } public String getName() { return name; } public intgetIq() { return iq; } public String toString() { return "Person{" + "name='" + name + '\'' + ", iq=" + iq +'}'; } }
Immutable Person Class in Scala case class Person(name: String, iq: Int)
Functions as first class citizens • Filter users based on subscription rules • 71 lines of PHP code vs. 10 lines below levelPrefix.foreach { prefix => valvalidLevelIds =allLevels.filter( _.name.startsWith(prefix) ).filter(levelTermAnnual.isEmpty || _.annual == levelTermAnnual.get).map(_.id) allIds= allIds.filter { case UserModel.UserIdData(id, account_id, email) => if(prefix == "free") !subscriptions.contains(account_id) else subscriptions.get(account_id).map(s => validLevelIds.contains(s.level_id)).getOrElse(false) } }
Does a string have an upper case character booleanhasUpperCase = false for (inti = 0; i< name.length(); i++) { if (Character.isUpperCase(name.charAt(i))) { hasUpperCase = true; break; } }
Does a string have an upper case character valhasUpperCase = name.exists(_.isUpper)
Benefits: Parallel Processing • Functional style encourages immutability • Actors and Futures provide a simpler mental model for concurrency • Using significant concurrency; haven’t had significant bugs related to it • Not the norm
Example: Processing pages //Send a message with each page valpageFutures = pages.map{ page => ask(pageRouter, new PageGeneratorMessage(page)).mapTo[Page] } //get the results valpdfPages= Future.sequence(pageFutures).map { future => future.getPdf } … //create actor class PageGeneratorextends Actor { def receive = { case m: PageGeneratorMessage => sender ! generatePage(m.getPage) case _ => Logger.error("Unknown message " + m.toString) } … }
Benefits: Type safe (with great inference) • A class of errors is guaranteed not to exist • More confident refactoring • Coming into a large codebase, types help with readability of code base
Benefits: Performance Image Generation service • Average response time decreased by 53% • Median response time decreased by 37%