The Future Starts with a Promise
Alexandru Nedelcu
bionicspirit.com
The Future Starts with a Promise
● Dumb title
● The Future starts whether we want it or not
Performance
“amount of useful work accomplished by a
computer system compared to the time and
resources used”
Performance
Currency for things we want
What we want
● Productivity
● Scalability
● Throughput
● Resiliency
● Low infrastructure costs
● ...
CPU bound
vs
I/O bound
Latency Comparison Numbers
gist.github.com/jboner/2841832
L1 cache reference                            0.5 ns
Branch mispredict                             5   ns
L2 cache reference                            7   ns  14x L1
Mutex lock/unlock                            25   ns
Main memory reference                       100   ns  20x L2, 200x L1
Compress 1K bytes with Zippy              3,000   ns
Send 1K bytes over 1 Gbps network        10,000   ns   
Read 4K randomly from SSD*              150,000   ns   
Read 1 MB sequentially from memory      250,000   ns   
Round trip within same datacenter       500,000   ns   
Read 1 MB sequentially from SSD*      1,000,000   ns  4X memory
Disk seek                            10,000,000   ns  
Read 1 MB sequentially from disk     20,000,000   ns  80x memory, 20X SSD
Send packet CA­>Netherlands­>CA     150,000,000   ns
Optimizing I/O bound operations
● For scalability the whole workflow must be
based on asynchronous I/O
– E.g. Slowloris HTTP DoS
– All Java Servlet Containers (up until Servlets 3.1)
are vulnerable
● Must avoid blocking threads in a limited
thread-pool
Going Forward
● Async I/O
● Non-blocking
● Event-driven
● Reactive
Example of Common Pattern
var result = "Not Initialized"
var isDone = false
val producer = new Thread(new Runnable {
    def run() {
        result = "Hello, World!"
        isDone = true
    }
})
val consumer = new Thread(new Runnable {
    def run() {
        // loops until isDone is true
        while (!isDone) {}
        println(result)
     }
})
Example of Common Pattern
var result = "Not Initialized"
var isDone = false
val producer = new Thread(new Runnable {
    def run() {
        result = "Hello, World!"
        isDone = true
    }
})
val consumer = new Thread(new Runnable {
    def run() {
        // loops until isDone is true
        while (!isDone) {}
        println(result)
     }
})
What does it print?
a) Hello world!
b) Not initialized
c) Nothing (infinite loop)
Example of Common Pattern
What does it print?
a) Hello world!
b) Not initialized
c) Nothing (infinite loop)
d) All of the above
var result = "Not Initialized"
var isDone = false
val producer = new Thread(new Runnable {
    def run() {
        result = "Hello, World!"
        isDone = true
    }
})
val consumer = new Thread(new Runnable {
    def run() {
        // loops until isDone is true
        while (!isDone) {}
        println(result)
     }
})
Example of Common Pattern
var result = "Not Initialized"
var isDone = false
val lock = new AnyRef
val producer = new Thread(new
Runnable {
def run() {
lock.synchronized {
result = "Hello, World!"
isDone = true
}
}
})
val consumer = new Thread(new
Runnable {
def run() {
var exitLoop = false
while (!exitLoop)
lock.synchronized {
if (isDone) {
println(result)
exitLoop = true
}
}
}
})
Locks
● Locks break encapsulation
● Locks do not compose
● It's easy to make mistakes
– Acquiring too few
– Acquiring too many
– Acquisition in the wrong order
java.util.concurrent.Future
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
(since Java 1.5)
java.util.concurrent.Future
import java.util.concurrent.{Callable, Executors, Future}
val pool = Executors.newCachedThreadPool()
// non­blocking
val future: Future[String] =
  pool.submit(new Callable[String] {
    def call() = {
      // process and return something
      "Hello World!"
    }
  })
// blocks, waiting for result...
val result = future.get()
println(result)
pool.awaitTermination(1, TimeUnit.SECONDS)
java.util.concurrent.Future
● Good API encapsulation
● General concept, the executor can be anything:
– a thread
– a thread-pool
– a process running on another machine
SpyMemcached Example
import java.util.concurrent.{TimeUnit, Future}
import net.spy.memcached.{AddrUtil, MemcachedClient}
val client = new MemcachedClient(
AddrUtil.getAddresses("127.0.0.1:11211")
)
// non-blocking
val f: Future[AnyRef] = client.asyncGet("greeting")
// blocking for the result
val obj = f.get(1, TimeUnit.SECONDS)
if (obj == null) {
println("Greeting not available!")
client.set("greeting", 10000, "Hello World!")
}
else
println(obj)
client.shutdown(1, TimeUnit.SECONDS)
java.util.concurrent.Future
public interface Future<V> {
boolean cancel(boolean);
boolean isCancelled();
boolean isDone();
V get();
V get(long, TimeUnit);
}
Problems?
java.util.concurrent.Future
public interface Future<V> {
boolean cancel(boolean);
boolean isCancelled();
boolean isDone();
V get();
V get(long, TimeUnit);
}
Problems
● Blocking
● Non-composable
Scala's Futures and Promises
● Designed as a part of Akka
● Integrated in Scala's standard library (SIP-14)
scala.concurrent.Future
trait Future[+T] extends Awaitable[T] {
abstract def isCompleted: Boolean
abstract def onComplete[U](func: Try[T] => U)
(implicit ec: ExecutionContext): Unit
abstract def value: Option[Try[T]]
// ...
}
scala.concurrent.Future
import concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import scala.util.{Failure, Success}
val f: Future[String] = Future {
"Hello, World!"
}
f.onComplete {
case Success(value) =>
println(value)
case Failure(exception) =>
System.err.println(exception.getMessage)
}
Blocking for the Result
import scala.concurrent.{Await, Future}
import scala.util.{Failure, Success}
import scala.concurrent.duration._
import concurrent.ExecutionContext.Implicits.global
val f: Future[String] = Future {
"Hello, World!"
}
val result = Await.result(f, 10.seconds)
println(result)
scala.concurrent.Future
● onComplete() is too low level
● Not composable
● Leads to callback hell
Future.foreach()
def squareRoot(x: Double) = Future {
math.sqrt(x)
}
squareRoot(3).foreach { x =>
println(x)
}
Future.foreach()
def squareRoot(x: Double) = Future {
math.sqrt(x)
}
for (x <- squareRoot(3)) {
println(x)
}
Future.map()
def squareRoot(x: Double) = Future {
math.sqrt(x)
}
squareRoot(3).map { x => x + 2 }
Future.map()
def squareRoot(x: Double) = Future {
math.sqrt(x)
}
for (x <- squareRoot(3)) yield x + 2
Future.filter()
import scala.concurrent.{Await, Future}
import concurrent.ExecutionContext.Implicits.global
import concurrent.duration._
val f = Future {
2
}
val r = f.filter(x => x % 2 == 1)
// throws java.util.NoSuchElementException:
// Future.filter predicate is not satisfied
Await.result(r, 1.second)
Future.filter()
import scala.concurrent.{Await, Future}
import concurrent.ExecutionContext.Implicits.global
import concurrent.duration._
val f = Future {
2
}
val r = for (x <- f; if x % 2 == 1) yield x
// throws java.util.NoSuchElementException:
// Future.filter predicate is not satisfied
Await.result(r, 1.second)
On Monads
● It's just a design pattern
On Monads
● It's just a design pattern
● A monad is basically a container, or a context
● Sometimes, when operating on values, you
want to keep the context
On Monads
● Future[T] is a monadic type
● It's a container
● It's a context
● It allows you to operate on values, even if their
processing finished or not (e.g. within the
Future context)
On Monads
● A monadic type M[T] must implement:
– a constructor
– map[U](f: T => U): M[U]
– filter(f: T => Boolean): M[T]
– ...
On Monads
● A monadic type M[T] must implement:
– a constructor
– map[U](f: T => U): M[U]
– filter(f: T => Boolean): M[T]
– Either of …
● flatten()
● flatMap[U](f: T => M[U]): M[U]
Future.flatMap()
val client = shade.Memcached(
Configuration("127.0.0.1:11211"),
ActorSystem("default").scheduler,
concurrent.ExecutionContext.Implicits.global
)
val f: Future[String] =
client.get[String]("username") flatMap {
case Some(value) =>
Future.successful("Hello, " + value + "!")
case None =>
client.set("username", "Alex", 30.seconds) map { _ =>
"Hello, Anonymous!"
}
}
Future.flatMap()
val username = client.get[String]("username")
val password = client.get[String]("password")
val userPass: Future[String] =
username.flatMap { user =>
password.map { pass =>
user.getOrElse("anonymous") +
":" +
pass.getOrElse("none")
}
}
Future.flatMap()
val username = client.get[String]("username")
val password = client.get[String]("password")
val userPass: Future[String] =
for (u <- username; p <- password) yield
u.getOrElse("anonymous") +
":" +
p.getOrElse("none")
Future.flatMap()
val user: Future[Option[User]] =
client.get[Option[User]]("user-" + id) flatMap {
// value found in cache
case Some(value) =>
Future.successful(value)
// value not found
case None =>
// fetching from DB
db.fetchUserBy(id) flatMap { value =>
// setting cache
client.set("user-" + id, value, 30.minutes)
.map(_ => value)
}
}
Future.recover()
val client = shade.Memcached(
Configuration("127.0.0.1:11211"),
ActorSystem("default").scheduler,
concurrent.ExecutionContext.Implicits.global
)
client.get[String]("something").recover {
case _: TimeoutException =>
None
}
Future.recoverWith()
googleMaps.search(lat, lon).recoverWith {
case _: APILimitException =>
bingMaps.search(lat, lon)
}
Future.sequence()
val searches = Seq(
googleMaps.search(lat, lon),
bingMaps.search(lat, lon),
geoNames.search(lat, lon)
)
val f: Future[Seq[Option[Location]]] =
Future.sequence(searches)
scala.concurrent.Promise
● The write-side of a Future
● An object which can be completed either with a
value or failed with an exception
scala.concurrent.Promise
import concurrent.Promise
val p = Promise[String]()
val f: Future[String] = p.future
// later after processing is done ...
p.success("Hello, World!")
Ning's AsyncHttpClient
import com.ning.http.client._
import concurrent._
class MyAsyncClient(underlying: AsyncHttpClient) {
def fetch(url: String) = {
val promise = Promise[String]()
val handler = new AsyncCompletionHandler[Unit]() {
def onCompleted(resp: Response) {
promise.success(resp.getResponseBodyAsString)
}
def onThrowable(ex: Throwable) {
promise.failure(ex)
}
}
val request = underlying.prepareGet("http://www.google.com/")
request.execute(handler)
promise.future
}
}
Timeout Sample
def withTimeout[T](future: Future, atMost: FiniteDuration) = {
val promise = Promise[T]()
future.onComplete { result =>
promise.tryComplete(result)
}
// schedule the timeout
val scheduler = ActorSystem("default").scheduler
scheduler.scheduleOnce(atMost) {
promise.tryFailure(new TimeoutException)
}
promise.future
}
val value = cacheClient.get[String]("something")
val timed = withTimeout(value, 10.seconds)
timed.recover {
case _: TimeoutException => None
}
Problems
● Debugging
● Callback hell still possible
– C# Async is nice
– https://github.com/scala/async
● Too basic for processing streams
– Iteratees → uses Future as a building block
Other Alternatives
● Guava
● C# Async / System.Threading.Tasks.Task
● Q (Javascript)
● Twisted Deferred (Python)
Further Learning
● Principles of Reactive Programming
(Coursera.org)
● Going Reactive (by Jonas Boner at ScalaDays)
● Futures and Promises (docs.scala-lang.org)
● Play Framework 2.x
Questions?

The Future starts with a Promise