functional programming and composing actors

Post on 15-Aug-2015

180 Views

Category:

Software

1 Downloads

Preview:

Click to see full reader

TRANSCRIPT

&√

Deputy CTO

Functional Programming

Composing Actors

&Functional Programming

without sacrificing Communication

Resilience√

Deputy CTO

4

Connectedness

«Software is becoming increasingly interconnected»

5

Klang’s Conjecture

«If you cannot solve a problem without programming; you cannot solve a problem with programming.»

Reality Imaginary>

My office door

7

Stunning views

Reality• separation in space & time gives us

• communication for coordination

• variable delays

• partial failures

• only partial/local/stale knowledge

• systems

8

9

system

«a set of things working together as parts of a mechanism or an interconnecting network;

a complex whole»

noun

Systems• Purpose is typically simple

• Complex inner workings

• Consist of collaborating components

• Components can be systems themselves

• Components are as simple as feasible, but not simpler

10

11

Resilience

«Any sufficiently complex system is always running in degraded mode»

— Richard I. Cook, MD “How complex systems fail” (paraphrased)

Communication• The production and consumption of messages

• Messaging implies that we need to be able to address recipients

• Addresses/Identities are important

• They are knowledge that can be shared

• Messages can become delayed, lost or misunderstood

12

Think: Reliability• Are we ever guaranteed delivery?

• at-most-once

• at-least-once

• exactly-once

• It’s not about guarantees,it’s about reliability

13

Two-Phased Commit

14

Burstiness• Most communication is bursty

• some is predictable

• some is unpredictable

• load shedding can cause burstiness

15

Flow control / Back pressure

16

• Buffers are only grease between cogs

• Does not solve overload problems

• Load shedding does not inform sender

• Reasons

• When shedding will end

www.reactive-­‐streams.org

Resilience• Never assume that other entities are immortal

• Treat expectation violations as failures

• Always have a Plan B

• Clients are not responsible to fix a faulty provider

• Fail fast & predictably

17

18

Supervision• Responsibility to deal with the failure/corruption of other

• Does not place the burden of fixing it on the collaborators

«Quis custodiet ipsos custodes?» — Decimus Iunius Iuvenalis

19

actors

• Akka's unit of computation is called an actor • Akka actors are purely reactive components: • Current behavior • Address • Local storage • Mailbox

• Scheduled to execute when sent a message • An actor has a parent actor, handling its failures • An actor may have 0..N child actors

20

« 2500 nodes × millions of actors per GB RAM = a lot»

actors

• An actor processes a message at a time • Multiple-producers & Single-consumer

• The overhead per actor is about ~450bytes • Run millions of actors on commodity hardware

• Akka Cluster currently handles ~2500 nodes

actors

21

actors

• Great for • Communication • Location transparency • Elasticity

• Resilience • Main challenge • Composition

Functional Compositiona → b → c

23

Functional Programming

• Great for • Composition

• Main challenge • Communication • Resilience

24

typed: On the horizon

• Project Gålbma

• distill an Actor to its essence: the Behavior

• everything is a message—for real this time

• remove the danger to close over Actor environment

• behavior composition

• completely pure Actor implementation,through a process algebra (inspired by Join Calculus)

Behavior is King

25

abstract class Behavior[T] { def management(ctx: ActorContext[T], msg: Signal): Behavior[T]

def message(ctx: ActorContext[T], msg: T): Behavior[T]

def narrow[U <: T]: Behavior[U] = this.asInstanceOf[Behavior[U]]}

Behavior example

26

object Server { sealed trait Command case class Get(id: Int)(val replyTo: ActorRef[Got]) extends Command case class Put(name: String, ref: ActorRef[OtherCommand]) extends Command

case class Got(id: Int, contents: Map[String, ActorRef[OtherCommand]])

val initial: Behavior[Command] = withMap(Map.empty)

private def withMap(map: Map[String, ActorRef[OtherCommand]]) = Total[Command] { case g @ Get(id) => g.replyTo ! Got(id, Map.empty) Same case Put(name, ref) => withMap(map.updated(name, ref)) }}

Most common dev task?

• Receive inputs

• Transform data

• Produce outputs

• Drink coffee

27

28

#undef STREAMS #define STREAMS• ephemeral, time-dependent, sequences of elements

• possibly unbounded in length

• in essence: transformation & transportation of data

«No man ever steps in the same river twice, for it's not the same river

and he's not the same man.» — Heraclitus

streams

i m m u t a b l e

REUSABLE c o m p o s a b l e c o o r d i n a t e d asynchronoustransformations

Getting data across an asynchronous b o u n d a r y

Getting data across an asynchronous b o u n d a r y with non-blockingback pressure

Reactive Streams Initiative

T H E

38

Requirements Push Pull

support potentially unbounded sequences 😃 😃

sender runs separately from receiver 😃 😃

rate of reception may vary from rate of sending 😃 😃

dropping elements should be a choice and not a necessity 💩 😃

minimal (if any) overhead in terms of latency and throughput 😃 💩

Comparing Push vs Pull

&39

Supply

Demand

40

Publisher Subscriber

data

demand

• “push” when subscriber is faster

• “pull” when publisher is faster

• switches automatically between both

• batching demand allows batching ops

41

DynamicPush–Pull

Reactive Streams

42

Requirements Push Pull Both

support potentially unbounded sequences 😃 😃 😃

sender runs separately from receiver 😃 😃 😃rate of reception may vary from rate of sending 😃 😃 😃

dropping elements should be a choice and not a necessity 💩 😃 😃

minimal (if any) overhead in terms of latency and throughput 😃 💩 😃

Comparing Push vs Pull vs Both🌟

Stream splitting

43

demand

data

splitting the data means merging the demand

Stream merging

44

merging the data means splitting the demand

Source Flow Sink

RunnableGraph

46

streams: Linear transformations

val fives = Source.repeat(5)

val timesTwo = Flow[Int].map(_ * 2)

val intToString = Flow[Int].map(_.toString)

val transform = timesTwo via intToString

val sysOut = Sink.foreach(println)

val r = fives via transform.take(10) to sysOut

r.run() // a Materializer needs to be in scope

BidiFlows

48

streams: Selected BidiFlows Examples

val codec: BidiFlow[Foo, ByteString, ByteString, Foo, Unit] = …val crypto:BidiFlow[ByteString, ByteString, ByteString, ByteString, Unit] = …val framing: BidiFlow[ByteString, ByteString, ByteString, ByteString, Unit] = …val protocol = codec atop crypto atop framing

DAG

DCGWAT

Fan-In

Fan-Out&

Fan-tastic!

52

streams: Cthulhu-Merge-Route Explained

OI

Materialization

55

streams: Materialization

• Akka Streams separates the what from the how & when

• declarative Source/Flow/Sink DSL to create a blueprint

• ActorMaterializer turns this into running Actors

• enables customizable materialization strategies

• optimization

• verification / validation

• distributed deployment

56

streams: MaterializedValues

• At Composition time

• MaterializedValues of respective sides are composed

• At Materialization time

• A value of the final type of the MaterializedValueof the graph is returned

57

streams: Composition

58

streams: Composition

val runnableGraph = FlowGraph.closed() { implicit builder => import FlowGraph.Implicits._

val A: Outlet[Int] = builder.add(Source.single(0)) val B: UniformFanOutShape[Int, Int] = builder.add(Broadcast[Int](2)) val C: UniformFanInShape[Int, Int] = builder.add(Merge[Int](2)) val D: FlowShape[Int, Int] = builder.add(Flow[Int].map(_ + 1)) val E: UniformFanOutShape[Int, Int] = builder.add(Balance[Int](2)) val F: UniformFanInShape[Int, Int] = builder.add(Merge[Int](2)) val G: Inlet[Any] = builder.add(Sink.foreach(println)) C <~ F A ~> B ~> C ~> F B ~> D ~> E ~> F E ~> G }

59

streams: Composition

Functional Programming

Akka Streams

Akka Typed

Functional Programming

60

streams: Future?

Functional Programming

Akka Streams

Akka Typed

Functional Programming

61

Summary

Akka Streams and Akka Typed promises both the compositional strengths of FP, paired with the communicational strengths and resilience of the Actor Model.

Opportunity: Self-tuning back pressure

63

• Each processing stage can know • Latency between requesting more and getting more • Latency for internal processing • Behavior of downstream demand • Latency between satisfying and receiving more • Trends in requested demand (patterns)

• Lock-step • N-buffered • N + X-buffered • “chaotic”

Opportunity: Operation elision

64

• Compile-time, using Scala Macros • fold ++ take(n where n > 0) == fold • drop(0) == identity • <any> ++ identity == <any>

• Run-time, using intra-stage simplification • map ++ dropUntil(cond) ++ take(N) • map ++ identity ++ take(N) • map ++ take(N)

Opportunity: Operation fusion

65

• Compile-time, using Scala Macros • filter ++ map == collect

• Run-time, using intra-stage simplification • Rule: <any> ++ identity == <any>

Rule: identity ++ <any> == <any> • filter ++ dropUntil(cond) ++ map • filter ++ identity ++ map == collect

Opportunity: Execution optimization

66

• synchronous intra-stage execution N steps then trampoline and/or give control to other Thread / Flow

67

public interface Publisher<T> { public void subscribe(Subscriber<T> s); } public void Subscription { public void request(long n); public void cancel(); } public interface Subscriber<T> { public void onSubscribe(Subscription s); public void onNext(T t); public void onError(Throwable t); public void onComplete(); } public interface Processor<T, R> extends Subscriber<T>, Publisher<R> { }

How does it connect?

68

SubscriberPublisher

subscribe(subscriber)

onSubscribe(subscription)

How does data flow?

69

SubscriberPublisher

subscription.request(1)

onNext(element)

subscription.request(3)

onNext(element)

subscription.request(1)

How does data flow?

70

SubscriberPublisher

onNext(element)

onNext(element)

subscription.request(2)

onNext(element)

onNext(element)

How does it complete?

71

SubscriberPublisher

subscription.request(1)

onNext(element)

onComplete()

onNext(element)

What if it fails?

72

SubscriberPublisher

subscription.request(1)

onNext(element)

subscription.request(5)

onError(exception)

73

Try Akka Streams: (1.0)https://github.com/typesafehub/activator-akka-stream-scala

References

Reactive Streams for JVMhttps://github.com/reactive-streams/reactive-streams-jvm

top related