Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
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 misp...
Optimizing I/O bound operations
● For scalability the whole workflow must be
based on asynchronous I/O
– E.g. Slowloris HT...
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 {
    d...
Example of Common Pattern
var result = "Not Initialized"
var isDone = false
val producer = new Thread(new Runnable {
    d...
Example of Common Pattern
What does it print?
a) Hello world!
b) Not initialized
c) Nothing (infinite loop)
d) All of the ...
Example of Common Pattern
var result = "Not Initialized"
var isDone = false
val lock = new AnyRef
val producer = new Threa...
Locks
● Locks break encapsulation
● Locks do not compose
● It's easy to make mistakes
– Acquiring too few
– Acquiring too ...
java.util.concurrent.Future
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelle...
java.util.concurrent.Future
import java.util.concurrent.{Callable, Executors, Future}
val pool = Executors.newCachedThread...
java.util.concurrent.Future
● Good API encapsulation
● General concept, the executor can be anything:
– a thread
– a threa...
SpyMemcached Example
import java.util.concurrent.{TimeUnit, Future}
import net.spy.memcached.{AddrUtil, MemcachedClient}
v...
java.util.concurrent.Future
public interface Future<V> {
boolean cancel(boolean);
boolean isCancelled();
boolean isDone();...
java.util.concurrent.Future
public interface Future<V> {
boolean cancel(boolean);
boolean isCancelled();
boolean isDone();...
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[...
scala.concurrent.Future
import concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import scala.ut...
Blocking for the Result
import scala.concurrent.{Await, Future}
import scala.util.{Failure, Success}
import scala.concurre...
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 concurr...
Future.filter()
import scala.concurrent.{Await, Future}
import concurrent.ExecutionContext.Implicits.global
import concurr...
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 val...
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 ...
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]
...
Future.flatMap()
val client = shade.Memcached(
Configuration("127.0.0.1:11211"),
ActorSystem("default").scheduler,
concurr...
Future.flatMap()
val username = client.get[String]("username")
val password = client.get[String]("password")
val userPass:...
Future.flatMap()
val username = client.get[String]("username")
val password = client.get[String]("password")
val userPass:...
Future.flatMap()
val user: Future[Option[User]] =
client.get[Option[User]]("user-" + id) flatMap {
// value found in cache...
Future.recover()
val client = shade.Memcached(
Configuration("127.0.0.1:11211"),
ActorSystem("default").scheduler,
concurr...
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)
)
...
scala.concurrent.Promise
● The write-side of a Future
● An object which can be completed either with a
value or failed wit...
scala.concurrent.Promise
import concurrent.Promise
val p = Promise[String]()
val f: Future[String] = p.future
// later aft...
Ning's AsyncHttpClient
import com.ning.http.client._
import concurrent._
class MyAsyncClient(underlying: AsyncHttpClient) ...
Timeout Sample
def withTimeout[T](future: Future, atMost: FiniteDuration) = {
val promise = Promise[T]()
future.onComplete...
Problems
● Debugging
● Callback hell still possible
– C# Async is nice
– https://github.com/scala/async
● Too basic for pr...
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)
● Futu...
Questions?
Upcoming SlideShare
Loading in …5
×

The Future starts with a Promise

5,543 views

Published on

Published in: Technology
  • Be the first to comment

The Future starts with a Promise

  1. 1. The Future Starts with a Promise Alexandru Nedelcu bionicspirit.com
  2. 2. The Future Starts with a Promise ● Dumb title ● The Future starts whether we want it or not
  3. 3. Performance “amount of useful work accomplished by a computer system compared to the time and resources used”
  4. 4. Performance Currency for things we want
  5. 5. What we want ● Productivity ● Scalability ● Throughput ● Resiliency ● Low infrastructure costs ● ...
  6. 6. CPU bound vs I/O bound
  7. 7. 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
  8. 8. 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
  9. 9. Going Forward ● Async I/O ● Non-blocking ● Event-driven ● Reactive
  10. 10. 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)      } })
  11. 11. 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)
  12. 12. 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)      } })
  13. 13. 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 } } } })
  14. 14. 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
  15. 15. 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)
  16. 16. 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)
  17. 17. 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
  18. 18. 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)
  19. 19. java.util.concurrent.Future public interface Future<V> { boolean cancel(boolean); boolean isCancelled(); boolean isDone(); V get(); V get(long, TimeUnit); } Problems?
  20. 20. 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
  21. 21. Scala's Futures and Promises ● Designed as a part of Akka ● Integrated in Scala's standard library (SIP-14)
  22. 22. 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]] // ... }
  23. 23. 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) }
  24. 24. 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)
  25. 25. scala.concurrent.Future ● onComplete() is too low level ● Not composable ● Leads to callback hell
  26. 26. Future.foreach() def squareRoot(x: Double) = Future { math.sqrt(x) } squareRoot(3).foreach { x => println(x) }
  27. 27. Future.foreach() def squareRoot(x: Double) = Future { math.sqrt(x) } for (x <- squareRoot(3)) { println(x) }
  28. 28. Future.map() def squareRoot(x: Double) = Future { math.sqrt(x) } squareRoot(3).map { x => x + 2 }
  29. 29. Future.map() def squareRoot(x: Double) = Future { math.sqrt(x) } for (x <- squareRoot(3)) yield x + 2
  30. 30. 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)
  31. 31. 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)
  32. 32. On Monads ● It's just a design pattern
  33. 33. 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
  34. 34. 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)
  35. 35. On Monads ● A monadic type M[T] must implement: – a constructor – map[U](f: T => U): M[U] – filter(f: T => Boolean): M[T] – ...
  36. 36. 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]
  37. 37. 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!" } }
  38. 38. 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") } }
  39. 39. 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")
  40. 40. 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) } }
  41. 41. 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 }
  42. 42. Future.recoverWith() googleMaps.search(lat, lon).recoverWith { case _: APILimitException => bingMaps.search(lat, lon) }
  43. 43. 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)
  44. 44. scala.concurrent.Promise ● The write-side of a Future ● An object which can be completed either with a value or failed with an exception
  45. 45. 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!")
  46. 46. 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 } }
  47. 47. 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 }
  48. 48. 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
  49. 49. Other Alternatives ● Guava ● C# Async / System.Threading.Tasks.Task ● Q (Javascript) ● Twisted Deferred (Python)
  50. 50. 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
  51. 51. Questions?

×