A talk about the implications and context around API design. How APIs come to be and how to understand them. This talk was delivered as opening keynote, setting the tone, for the ScalaSwarm conference in Porto, Portugal in 2017.
Elevate Developer Efficiency & build GenAI Application with Amazon Q
ScalaSwarm 2017 Keynote: Tough this be madness yet theres method in't
1. Though this be madness
yet there’s method in’t
Konrad `ktoso` Malawski @ ScalaSwarm @ Porto 2017
2. Though this be madness
yet there’s method in’t
Hamlet quote
Konrad `ktoso` Malawski @ ScalaSwarm @ Porto 2017
3. Though this be madness
yet there’s method in’t
Samurai
Konrad `ktoso` Malawski @ ScalaSwarm @ Porto 2017
Hamlet quote
4. Though this be madness
yet there’s method in’t
Samurai
Konrad `ktoso` Malawski @ ScalaSwarm @ Porto 2017
Hamlet quote
ARGH!
THIS SLIDE DOES NOT MAKE ANY SENSE!
5. Though this be madness
yet there’s method in’t
Samurai
Konrad `ktoso` Malawski @ ScalaSwarm @ Porto 2017
Hamlet quote
or does it…?
ARGH!
THIS SLIDE DOES NOT MAKE ANY SENSE!
6. “Though this be madness
yet there’s method in’t”
Quote of Polonius,
Act II, Scene II
Hamlet, 1602
by William Shakespeare
“Throne of Blood”
Akira Kurosawa, 1957 Transposes plot of Macbeth
(by Shakespeare)
to Feudal Japan
Also adapted Hamlet, in “The Bad sleep Well”
however screenshot would not contrast so well…
7. Konrad `ktoso` Malawski @ ScalaSwarm @ Porto 2017
Though this be madness
yet there’s method in’t
11. Konrad `@ktosopl` Malawski
work: akka.io lightbend.com
personal blog: http://kto.so
communities: geecon.org Java.pl / KrakowScala.pl sckrk.com GDGKrakow.pl lambdakrk.pl
12. The concurrent & distributed
applications toolkit
Akka is a toolkit and runtime for
building highly concurrent, distributed,
and resilient message-driven
applications on the JVM
Akka
15. A thing about nulls
"I call it my billion-dollar mistake."
Sir C.A. R. Hoare, on his invention of the null reference
16. A thing about nulls
something.calculateSum(2, 2)
What does this code do?
17. A thing about nulls
something.calculateSum(2, 2)
What does this code do?
a) return 4
b) NullPointerException!
c) System.exit(0) // though I have a love-hate relationship with this answer…
18. The curious case of Options
Scala Option – 2007 (Scala 2.5, sic! http://www.scala-lang.org/old/node/165)
Guava Optional – 2011 (since v10.0)
Java Optional – 2014 (since v1.8)
19. Scala Option – 2007 (Scala 2.5, sic! http://www.scala-lang.org/old/node/165)
Guava Optional – 2011 (since v10.0)
Java Optional – 2014 (since v1.8)
The curious case of Options
sealed abstract class Option[+A]
extends Product with Serializable { self =>
def isEmpty: Boolean
def isDefined: Boolean = !isEmpty
def get: A
@inline final def getOrElse[B >: A](default: => B): B =
if (isEmpty) default else this.get
20. Scala Option – 2007 (Scala 2.5, sic! http://www.scala-lang.org/old/node/165)
Guava Optional – 2011 (since v10.0)
Java Optional – 2014 (since v1.8)
The curious case of Options
sealed abstract class Option[+A]
extends Product with Serializable { self =>
def isEmpty: Boolean
def isDefined: Boolean = !isEmpty
def get: A
@inline final def getOrElse[B >: A](default: => B): B =
if (isEmpty) default else this.get
public final class Optional<T> {
public boolean isPresent() {
return value != null;
}
public T get() {
if (value == null)
throw new NoSuchElementException("No value present");
return value;
}
public T orElseGet(Supplier<? extends T> other) {
return value != null ? value : other.get();
}
21. The curious case of Options
val o: Option[String] = ???
o.foreach(_.toUpperCase(Locale.ROOT)) // ok, sure
o match {
case Some(value) => value.toUpperCase(Locale.ROOT)
case None => "_"
}
We all have the same “mistake”:
get seems innocent, but it’s
not…
final Optional<String> optional = Optional.of("");
optional.map(it -> it.toUpperCase(Locale.ROOT));
if (optional.isPresent()) {
optional.get().toUpperCase();
}
22. Can we do better than that though?
“What the eyes don’t see,
the programmer does not invoke.”
23. We hide the “bad” API somewhere…
where it’s a bit harder to reach?
Analysis: Scala Future APIs
Separated API?
25. Blocking is the new “you broke the build!”
// BAD! (due to the blocking in Future):
implicit val defaultDispatcher = system.dispatcher
val routes: Route = post {
complete {
Future { // uses defaultDispatcher
Thread.sleep(5000) // will block on the default dispatcher,
System.currentTimeMillis().toString // starving the routing infra
}
}
}
26. Blocking is the new “you broke the build!”
// BAD! (due to the blocking in Future):
implicit val defaultDispatcher = system.dispatcher
val routes: Route = post {
complete {
Future { // uses defaultDispatcher
Thread.sleep(5000) // will block on the default dispatcher,
System.currentTimeMillis().toString // starving the routing infra
}
}
}
http://stackoverflow.com/questions/34641861/akka-http-blocking-in-a-future-blocks-the-server/34645097#34645097
28. The curious case of Futures
public class CompletableFuture<T> implements Future<T>, CompletionStage<T> {
public T get() throws InterruptedException, ExecutionException {
// ...
}
public T get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
// ...
}
29. The curious case of Futures
Anyone remember the days before
scala.concurrent.Future?
Back to the Future, in which we discuss Akka and Twitter Futures in 2012 :-)
https://groups.google.com/forum/?fromgroups=#!topic/akka-user/eXiBV5V7ZzE%5B1-25%5D
30. The curious case of Futures
public class CompletableFuture<T> implements Future<T>, CompletionStage<T> {
public T get() throws InterruptedException, ExecutionException {
// ...
}
public T get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
// ...
}
trait Future[+T] extends Awaitable[T] {
// THERE IS NO GET!
// Closest thing to it is...
def value: Option[Try[T]]
// However it’s not that widely known actually
// Notice that it is non-blocking!
31. The curious case of Futures
public class CompletableFuture<T> implements Future<T>, CompletionStage<T> {
public T get() throws InterruptedException, ExecutionException {
// ...
}
public T get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
// ...
}
trait Future[+T] extends Awaitable[T] {
// THERE IS NO GET!
// Closest thing to it is...
def value: Option[Try[T]]
// However it’s not that widely known actually
// Notice that it is non-blocking!
32. The curious case of Futures
public class CompletableFuture<T> implements Future<T>, CompletionStage<T> {
public T get() throws InterruptedException, ExecutionException {
// ...
}
public T get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
// ...
}
trait Future[+T] extends Awaitable[T] {
// THERE IS NO GET!
// Closest thing to it is...
def value: Option[Try[T]]
// However it’s not that widely known actually
// Notice that it is non-blocking!
object Await {
@throws(classOf[TimeoutException])
@throws(classOf[InterruptedException])
def ready[T](awaitable: Awaitable[T], atMost: Duration): awaitable.type =
blocking(awaitable.ready(atMost)(AwaitPermission))
@throws(classOf[Exception])
def result[T](awaitable: Awaitable[T], atMost: Duration): T =
blocking(awaitable.result(atMost)(AwaitPermission))
}
35. It’s not like we never thought about Types.
Any => Unit
36. It’s not like we never thought about Types.
(like… 3+ years of developing Akka Streams)
Any => Unit
37. The journey to Akka TypedOne actor is no Actor
The Actor model is strictly about:
1. React on message
2. Send finite number of messages
3. Spawn child Actor
def receive: Any => Unit = {
case WorkNow(id) =>
sender() ! “okey!”
context.become(working(id))
}
39. The journey to Akka Typed
Ancient API, deprecated“Typed Actor” API
Goal was to expose what Java developers knew.
40. The journey to Akka Typed
Old “TypedActor” experimental in 2.3, removed
Upsides:
- Easily bridge to “non-Akka” / “non-Reactive” apps
- type-safe
- “easy” (not necessarily a good thing)
Downsides:
- Reflection, 10x slow-down compared to UntypedActor
- “RPC”-ish, not true to the core messaging
- Not true to Akka’s core principle: Messaging
42. The journey to Akka Typed
“Typed Channels” experimental in 2.3, removed
43. The journey to Akka Typed
“Typed Channels” experimental in 2.3, removed
Upsides:
- completely type-safe
- very expressive
Downsides:
- Too complex, many new operators
- Had to rely on scala macros
- “sender” difficult to solve
47. Akka Typed
try it now, 2.5.2
2 styles, 100% awesome.
Full Java & Scala API, as usual.
Actor.mutable – similar to current Actors, Behavior is a class
Actor.immutable – more functional style, recommended
48. Akka Typed
Main user-facing changes:
ActorRef[T] typed ActorRefs.
Core concept is Behavior[T]
which can be freely composed.
You always “become(Behavior)”, by returning Behavior.
sender() is gone,
not possible to type it well.
sender was trouble anyway, so that’s good!
53. Akka Typed
Actor.immutable (Scala)
Don’t worry, Java will eventually get pattern matching:
http://mail.openjdk.java.net/pipermail/amber-spec-experts/2017-April/000033.html
Java adopting Scala features confirms Scala’s design.
…but, until then we provide you with helpers and DSLs:
55. Akka Typed
try it now, 2.5.2
Learn more:
from the docs:
http://doc.akka.io/docs/akka/snapshot/scala/typed.html
and the blog:
1.Akka Typed: Hello World in the new API
2.Akka Typed: Coexistence
3.Akka Typed: Mutable vs. Immutable
4.Akka Typed: Protocols
5. Akka Typed: Supervision
6.Akka Typed: Lifecycle and Watch
7.Akka Typed: Timers
74. “Squiggle Kingdom”
What do you think this API was designed for?
What’s the context?
He certainly overdid it there :-)
“Squiggle Kingdom”“Squiggle kingdom”
75. “Squiggle Kingdom”
An API to look like Graphs drawn on whiteboard.
“Squiggle Kingdom”“Squiggle kingdom”
76. “Squiggle Kingdom”
An API to look like Graphs drawn on whiteboard.
“Squiggle Kingdom”“Squiggle kingdom”
77. “Squiggle Kingdom”
An API to look like Graphs drawn on whiteboard.
BidiFlow.fromGraph(GraphDSL.create() { implicit b =>
import GraphDSL.Implicits._
val netIn = new Inlet[ByteString]("netIn")
val netOut = new Outlet[ByteString]("netOut")
val httpIn = new Inlet[HttpRequest]("httpIn")
val httpOut = new Outlet[HttpResponse]("httpOut")
httpIn <~ requestPrep <~ controller.requestOut; controller.requestIn <~ requestParsing <~ netIn
httpOut ~> controller.responseIn; controller.responseOut ~> rendererPipeline ~> netOut
BidiShape(netIn, netOut, httpIn, httpOut)
})
“Squiggle Kingdom”“Squiggle kingdom”
78. “Squiggle Kingdom”
Very rarely used “super-power-user” API.
Normally just stick to:
val adding = Flow[Int].map(_ + 1)
val source = Source.maybe[Int].via(adding).map(_ - 1)
source.runWith(Sink.foreach(println))
“Squiggle Kingdom”“Squiggle kingdom”
79. “Squiggle Kingdom”
Very rarely used “super-power-user” API.
Normally just stick to:
val adding = Flow[Int].map(_ + 1)
val source = Source.maybe[Int].via(adding).map(_ - 1)
source.runWith(Sink.foreach(println))
Why did we pick those 3 different words?
“Squiggle Kingdom”“Squiggle kingdom”
82. Akka Actor in one sentence:
Messaging as a core abstraction, not slap-on afterthought.
Messages! Not method calls.
Akka in one sentence:
A toolkit for building highly distributed and concurrent apps.
84. Messages! Not method calls.
Waldo J,Wyant G,Wollrath A, Kendall S.A @ Sun Microsystems Laboratories. 1994.
Note on Distributed Computing
85. Messages! Not method calls.
Methods: // locally:
val value: Long = local.calculateSum(2, 2)
// if it’s parallel then we need some middle man to handle concurrency issues hm…
// but remote will have big latency so...
val value: Future[Long] = remote.calculateSum(2, 2)
// Q1: what is actually put on the wire?
// Q2: what about retrying to different host,
// - now need magic to handle it...
// Q3: can the downstream directly respond to upstream?
// - ok, so we could build a special method that does this
// Q4: what if the networking breaks...? Do I need to try/catch?
// ... but why, if it could be a simple message send :-)
86. Messages! Not method calls.
Methods: // locally:
val value: Long = local.calculateSum(2, 2)
// if it’s parallel then we need some middle man to handle concurrency issues hm…
// but remote will have big latency so...
val value: Future[Long] = remote.calculateSum(2, 2)
// Q1: what is actually put on the wire?
// Q2: what about retrying to different host,
// - now need magic to handle it...
// Q3: can the downstream directly respond to upstream?
// - ok, so we could build a special method that does this
// Q4: what if the networking breaks...? Do I need to try/catch?
// ... but why, if it could be a simple message send :-)
Messages: // locally:
local ! CalculateSum(2, 2)
// I'll get a reply in a bit, or I can retry etc etc.
// Actor is can be running in parallel, on multi-core etc, same API.
// what can happen?
// JVM can crash => effectively message loss
// remotely
remote ! CalculateSum(2, 2)
// what can happen?
// message loss
// receiver of the msg can crash... => message loss
87. Messages! Not method calls.
class OneWayProxyActor extends Actor {
val downstream: ActorRef = ???
def receive = {
case StateQuery(q) =>
sender() ! run(q)
case req: IncomingRequest =>
downstream forward transformed(req)
}
def run(any: Any) = ???
}
90. Messages! Not method calls.
I can totally do the same
with REST HTTP calls!
Sure you can (201, 202, 204), but:
In Akka that’s both default,
and exactly 400bytes cheap.
93. But it’s “only for testing”!
Messages! Not exposing state.
getter
class ComplexLogic {
def add(i: Item): ComplexLogic
def apply(): Effect
def state: State
}
94. Messages! Not exposing state.
val logicActor: ActorRef = ???
// no way to access state - for your own good.
logicActor ! Add(item)
logicActor ! Add(item)
logicActor ! Apply
expectMsgType[StateAppliedSuccessfully]
Test:
yes: behaviour and interactions,
not: raw state.
95. One of the 2 hardest problems
in computer science!
…and cache invalidation,
Naming things
96. One of the 2 hardest problems
in computer science!
…and cache invalidation,
…and 1-off errors.
Oh…
Naming things
97. “I want my words back.”
Roland Kuhn
(former Akka Team lead)
98. We want our words back…!
Two examples of name-collisions causing
confusion:
“Scheduler”
&
“Stream”
Naming is hard…
100. “Scheduler” - but is it the right one?
class CountdownActor extends Actor {
val untilEndOfWorld = 127.days // according to mayan prophecy
// oh, great, a scheduler!
val scheduler = context.system.scheduler
scheduler.scheduleOnce(untilEndOfWorld, self, EndOfTheWorld)
def receive = { case EndOfTheWorld => println("Aaaaa!!!") }
}
http://doc.akka.io/docs/akka/2.4.4/scala/scheduler.html#scheduler-scala
101. “Scheduler” - but is it the right one?
class CountdownActor extends Actor {
val untilEndOfWorld = 127.days // according to mayan prophecy
// oh, great, a scheduler!
val scheduler = context.system.scheduler
scheduler.scheduleOnce(untilEndOfWorld, self, EndOfTheWorld)
def receive = { case EndOfTheWorld => println("Aaaaa!!!") }
}
108. It’s a plain “Hashed Wheel Timer” though!
Optimised for:
• high performance
• lockless insertion
• O(1) insertion
• huge amounts of tasks
// timeouts - on each request, ask timeouts
Original white paper: Hashed and Hierarchical Timing Wheels:
https://pdfs.semanticscholar.org/0a14/2c84aeccc16b22c758cb57063fe227e83277.pdf
109. It’s a plain “Hashed Wheel Timer” though!
Optimised for:
• high performance
• lockless insertion
• O(1) insertion
• huge amounts of tasks
// timeouts - on each request, ask timeouts
Not for:
•preciseness
•persistence
Original white paper: Hashed and Hierarchical Timing Wheels:
https://pdfs.semanticscholar.org/0a14/2c84aeccc16b22c758cb57063fe227e83277.pdf
110. Akka’s Scheduler fun facts!
• Akka impl. after Netty implementation
• Akka impl. improved performance quite a bit
• Netty pulled-in the optimisations
(just one of multiple (both way) healthy interactions)
=> Yay, healthy Open Source ecosystem!
111. Akka’s Scheduler fun facts!
Error message you’ll likely never see:
LightArrayRevolverScheduler for short: “LARS”
112. Akka’s Scheduler fun facts!
Error message you’ll likely never see:
LightArrayRevolverScheduler for short: “LARS”
“LARS cannot start new thread,
ship’s going down!”
114. Persistent, reliable job scheduler: Chronos
Ok, so what do we need?
• Persistence
• Replication
• Consensus on who executes
115. Persistent, reliable job scheduler: Chronos
Optimised for:
• ”Like CRON, but distributed”
• Visibility, including management UI
• Retries of failed tasks
• Far-out tasks (usually measured in days etc)
• Consensus on who executes
• Also considered Lightweight
• 2k lines of Scala
• in it’s context… well, it is!
https://www.youtube.com/watch?v=FLqURrtS8IA
116. Persistent, reliable job scheduler: Chronos
Definitely NOT for:
• millions of tasks inserted per second
• to be run in the next second
… that’s what the the Akka scheduler is for!
117. Persistent, reliable job scheduler: Chronos
Definitely NOT for:
• millions of tasks inserted per second
• to be run in the next second
… that’s what the the Akka scheduler is for!
Notable mention: Quartz
(well known long-term scheduler,
with Akka integration and persistence)
118. “Stream”
What does it mean?!
* when put in “” the word does not appear in project name, but is present in examples / style of APIs / wording.
119. Suddenly everyone jumped on the word “Stream”.
Akka Streams / Reactive Streams started end-of-2013.
“Streams”
* when put in “” the word does not appear in project name, but is present in examples / style of APIs / wording.
120. Suddenly everyone jumped on the word “Stream”.
Akka Streams / Reactive Streams started end-of-2013.
The word “Stream” is used in many contexts/meanings
Akka Streams
Reactive Streams
RxJava “streams”*
Spark Streaming
Apache Storm “streams”*
Java Steams (JDK8)
Reactor “streams”*
Kafka Streams
ztellman / Manifold (Clojure)
* when put in “” the word does not appear in project name, but is present in examples / style of APIs / wording.
Apache GearPump “streams”
Apache [I] Streams (!)
Apache [I] Beam “streams”
Apache [I] Quarks “streams”
Apache [I] Airflow “streams” (dead?)
Apache [I] Samza
Scala Stream
Scalaz Streams, now known as FS2
Swave.io
Java InputStream / OutputStream / … :-)
2017年: 安定版。リアクティブストリーム付きの JDK9。
121. “Stream”
What does it mean?!
• Possibly infinite datasets (“streams”)
• “Streams are NOT collections.”
• Processed element-by-element
• Element could mean “byte”
• More usefully though it means a specific type “T”
• Asynchronous processing
• Asynchronous boundaries (between threads)
• Network boundaries (between machines)
2017年: 安定版。リアクティブストリーム付きの JDK9。
122. Where does Akka Stream fit?
Akka Streams specifically fits,
if you answer yes to any of these:
• Should it take on public traffic?
• Processing in hot path for requests?
• Integrate various technologies?
• Protect services from over-load?
• Introspection, debugging, excellent Akka integration?
• (vs. other reactive-stream impls.)
123. How do I pick which “streaming” I need?
Kafka serves best as a transport
for pub-sub across services.
• Note that Kafka Streams (db ops are on the node
is rather, different than the Reactive Kafka client
• Great for cross-service communication
instead of HTTP Request / Reply
Kafka はサービス間の pub-sub 通信に向いている
HTTP の代わりにサービス間の通信に使う
124. How do I pick which “streaming” I need?
Spark has vast libraries for ML or join etc ops.
• It’s the “hadoop replacement”.
• Spark Streaming is windowed-batches
• Latency anywhere up from 0.5~1second
• Great for cross-service communication
instead of HTTP Req/Reply
Spark は機械学習系が充実している
125. Oh yeah, there’s JDK8 “Stream” too!
Terrible naming decision IMHO, since Java’s .stream()
• Geared for collections
• Best for finite and known-up-front data
• Lazy, sync/async (async rarely used)
• Very (!) hard to extend
It’s the opposite what we talk about in Streaming
systems!
It’s more:“bulk collection operations”
Also known as… Scala collections API (i.e. Iterator
JDK8 の Stream はイテレータ的なもの
126. What about JDK9 “Flow”?
JDK9 introduces java.util.concurrent.Flow
• Is a 1:1 copy of the Reactive Streams interfaces
• On purpose, for people to be able to impl. it
• Does not provide useful implementations
• Is only the inter-op interfaces
• Libraries like Akka Streams implement RS,
and expose useful APIs for you to use.
JDK9 の Flow はリアクティブ・ストリーム
127. A core feature not obvious to the untrained eye…!
Akka Streams / HTTP
Quiz time!
TCP is a ______ protocol?
128. A core feature not obvious to the untrained eye…!
Akka Streams / HTTP
Quiz time!
TCP is a STREAMING protocol!
129. Streaming from Akka HTTP
No demand from TCP
=
No demand upstream
=
Source won’t generate tweets
130. Streaming from Akka HTTP
No demand from TCP
=
No demand upstream
=
Source won’t generate tweets
=>
Bounded memory
stream processing!
135. Free e-book and printed report.
bit.ly/why-reactive
Covers what reactive actually is.
Implementing in existing architectures.
Thoughts from the team that’s building
reactive apps since more than 6 years.
Obligatory “read my book!” slide :-)