Scalable Applications with Scala

Preview:

Citation preview

Scalable Applications

with Scala

Nimrod Argov

Tikal Knowledge

What is a Scalable

Application?

What is a Scalable

Application?

Scalability is the ability of a system,

network, or process to handle a growing

amount of work in a capable manner or its

ability to be enlarged to accommodate that

growth.

-- wikipedia.org

What is a Scalable

Application?

Administrative - More

People using the

system, or more

programmers writing it

What is a Scalable

Application?

Functional - Adding new

functionality with minimal

effort

What is a Scalable

Application?

Geographical - Maintaining

performance, usefulness or

usability regardless of locale

specific user concentration

What is a Scalable

Application?

Load - The ability of a

system to expand and

contract to accommodate

heavier or lighter loads

Concurrency /

Parallelism

Optimizing CPU Usage Efficiency

Scala

Scala brings together the Functional and Object

Oriented worlds, where until now both sides

considered the other to be totally wrong, or the

work of the devil.

-- Martin Oderski

Fun!

Java

java.util.concurrent

Locking

Mutable Shared State

Java

When is it enough?

Java

When is it enough?

Too Little - Race Conditions

Too Much - Loss of Parallelism / Deadlocks

Immutability

Concurrency requires Immutability

Classes should be immutable unless there's a very good reason to

make them mutable....If a class cannot be made immutable, limit

its mutability as much as possible.

-- Effective

Java (Joshua Bloch)

Immutability

//java

public final class Student

private final int id;

private final String name;

public Student(int id, String name){

this.id = id;

this.name = name;

}

.. getters…

}

Immutability

//java

public final class Student

private final int id;

private final String name;

public Student(int id, String name){

this.id = id;

this.name = name;

}

.. getters…

}

Immutability

//java

List<Student> students = new ArrayList<Student>();

List<Student> immutableStudents = Collections.unmodifiableList(students);

List<Students> ohBother = new CopyOnWriteArrayList<Student>();

Immutability

//java

List<Student> students = new ArrayList<Student>();

List<Student> immutableStudents = Collections.unmodifiableList(students);

students.add(new Student(15, “Harry Potter”));

immutableStudents now shows a different list!

Scala Collections

● Mutable/Immutable variants

● Immutable variant is imported by default

● Operations are Fast

● Cost of object creation is highly exaggerated by

programmers

Scala Collections

● Set

● Map

● List

● Stream

● Vector

● Stack

● Queue

● Tree

Scala Collections

Many Operations:

● ++ (add all)

● collect

● combinations

● contains

● count

● diff

● endsWith

● filter

● find

● flatMap

● flatten

● forEach

● intersect

● lift

● min

● max

● mkString

● partition

● permutations

● reduce

● reverse

Scala Parallel

Collections

Motivation

“The hope was, and still is, that implicit parallelism behind

a collections abstraction will bring reliable parallel

execution one step closer to the workflow of mainstream

developers.”

--Scala Documentation

Scala Parallel

Collections

Example: Calculate function over list of numbers

Scala Parallel

Collections

Example: Calculate function over list of numbers

Java:

// Somehow cut the list to N parts

Scala Parallel

Collections

Example: Calculate function over list of numbers

Java:

// Somehow cut the list to N parts

// Create ForkJoinTask (or Just a Runnable if pre JDK 6)

Scala Parallel

Collections

Example: Calculate function over list of numbers

Java:

// Somehow cut the list to N parts

// Create ForkJoinTask (or Just a Runnable if pre JDK 6)

// Use ForkJoinPool to run the tasks

// Wait for all to finish (join)

Scala Parallel

Collectionspublic class Operation extends RecursiveAction{

private List<Integer> numbers;

private List<Integer> results;

private int start, end;

public Operation(List<Integer> nums){

numbers = nums;

start = 0;

end = numbers.size();

}

protected void doComputation(){

for(int i = start; i < end; i++)

results.set(i, calculate(number));

}

}

Scala Parallel

Collectionspublic class Operation extends RecursiveAction{

protected static int threshold = 1000;

...

public Operation(List<Integer> nums){ … }

protected void doComputation(){ … }

protected void compute(){

if (numbers.length < threshold)

doComputation();

else

invokeAll(new SumOperation(numbers, 0,

numbers.size() / 2),

new SumOperation(numbers, numbers.size() /

2, numbers.size());

}

}

Scala Parallel

Collections

val myParallelList = someList.par

myParallelList map calculate(_)

Scala Parallel

Collections

val myParallelList = someList.par

myParallelList map calculate(_)

5 8 12 7 13 86 14 2 37 23 67 41 1 77 24 95

Scala Parallel

Collections

val myParallelList = someList.par

myParallelList map calculate(_)

5 8 12 7 13 86 14 2 37 23 67 41 1 77 24 95

5 8 12 7 13 86 14 2 37 23 67 41 1 77 24 95

Scala Parallel

Collections

val myParallelList = someList.par

myParallelList map calculate(_)

5 8 12 7 13 86 14 2 37 23 67 41 1 77 24 95

5 8 12 7 13 86 14 2 37 23 67 41 1 77 24 95

11 22 66 11 55 22 66 55 99 33 88 88 55 22 77 11

Scala Parallel

Collections

val myParallelList = someList.par

myParallelList map calculate(_)

5 8 12 7 13 86 14 2 37 23 67 41 1 77 24 95

5 8 12 7 13 86 14 2 37 23 67 41 1 77 24 95

11 22 66 11 55 22 66 55 99 33 88 88 55 22 77 11

11 22 66 11 55 22 66 55 99 33 88 88 55 22 77 11

Scala Parallel

Collections

• ParArray

• ParVector

• mutable.ParHashMap

• mutable.ParHashSet

• immutable.ParHashMap

• immutable.ParHashSet

• ParRange

• ParTrieMap

Scala Parallel

Collections

Each collection defines:

● Splitter

● Combiner

Default # of threads: how many cores have you got?

Underlying implementation depends on configuration

Scala Parallel

Collections

ForkJoinTaskSupport (JDK 1.6 or higher)

ThreadPoolTaskSupport

ExecutionContextTaskSupport

Scala Parallel

Collections

<<< WARNING… WARNING… >>>

Side Effects or Non-Associative operations will create

non-deterministic results!

Scala Parallel

Collections

var sum = 0;

List(1 to 10000).par.forEach(sum += _)

List(1 to 10000).par.reduce(_-_)

Scala Futures

Futures provide a nice way to reason about performing

many operations in parallel, in an efficient and non-

blocking way.

-- Scala Documentation

Scala Futures

Future Promise

Scala Futures

val p = Promise[T]

val f: Future[T] = p.future

Java Futures

val p = Promise[T]

val f: Future[T] = p.future

// Java-like variant:

doSomeWork()

f.get()

Efficient?

Efficient?

Also, Java Futures are NOT Composable

What We Want

Callbacks

Thread 1

val p = Promise[T]

val f: Future[T] = p.future

Thread 2

f onComplete {

case Success(calcResult) = println(calcResult)

case Failure(t) = println(“Error: “ + t.getMessage)

}

Callbacks

Thread 1

val p = Promise[T]

val f: Future[T] = p.future

Thread 2

f onComplete{

case Success(calcResult) = println(calcResult)

case Failure(t) = println(“Error: “ + t.getMessage)

}

Try

Try[T]

Success[T](value: T)

Failure[T](t: throwable)

Try

Try[T]

Success[T](value: T)

Failure[T](t: throwable)

Not Asynchronous

Try

val result: Try[int] = calcSomeValue

result match{

case Success(n) = println(“The result is “ + n)

case Failure(t) = println(“Error: “ + t.getMessage)

}

Monadic Combinators

val result: Int = calcSomeValue getOrElse -1

calcSomeValue.map(doSomethingWithVal).recover

(handleException)

val rateQuote = future {

service.getCurrentValue(USD)

}

val purchase = rateQuote map { quote =>

if (isProfitable(quote)) service.buy(amount, quote)

else throw new Exception("not profitable")

}

purchase onSuccess {

case _ => println("Purchased " + amount + " USD")

}

Future Composition

val usdQuote = future { service.getCurrentValue(USD) }

val chfQuote = future { service.getCurrentValue(CHF) }

val purchase = for {

usd <- usdQuote

chf <- chfQuote

if isProfitable(usd, chf)

} yield service.buy(amount, chf)

purchase onSuccess {

case _ => println("Purchased " + amount + " CHF")

}

Future Composition

def generate = future { (random, random) }

def calculate(x:(Double, Double)) = future{ pow(x._1, x._2) }

def filter(y:Any) = y.toString.contains("22222")

while (true) {

for {

i <- generate

j <- calculate(i)

if filter(j)

}

println(j)

}

Pipe Example

Recommended