Slide 1

Slide 1 text

Philipp Haller Serverless Cloud Computing Beyond FaaS: Programming Models and Abstractions Philipp Haller KTH Royal Institute of Technology Stockholm, Sweden 2nd Vienna Software Seminar (VSS) Vienna, Austria, Aug 29, 2019 1

Slide 2

Slide 2 text

Philipp Haller Background Scala 2 2005-2014 Scala language team 2012-2014 Typesafe, Inc. Co-author Scala language specification 2019: ACM SIGPLAN Programming Languages Software Award for Scala Core contributors:
 Martin Odersky, Adriaan Moors, Aleksandar Prokopec, Heather Miller, Iulian Dragos, Nada Amin, Philipp Haller, Sebastien Doeraene, Tiark Rompf

Slide 3

Slide 3 text

3 Scala Actors and Akka https://www.lightbend.com/akka-five-year-anniversary Scala Actors used, e.g., in core message queue system of Twitter:

Slide 4

Slide 4 text

Philipp Haller The use of actors is common in industry Side remark 4 Slide from: Meiklejohn et al. “Partisan” at USENIX ATC ‘19

Slide 5

Slide 5 text

Philipp Haller Ongoing work Current directions Type systems 5 LaCasa: lightweight affine types and object capabilities in Scala
 [Haller & Loiko 2016] Static reasoning about capabilities and resources Types for safe distribution Closures [Miller et al. 2014], eventual consistency [Zhao & Haller 2019] Reusability! Concurrent and distributed programming Deterministic concurrency [Haller et al. 2016], function passing [Haller et al. 2018], asynchronous streams [Haller & Miller 2019]

Slide 6

Slide 6 text

Philipp Haller Cloud computing Context Public cloud infrastructure integral part of numerous large-scale, commercial applications. Support for enterprise services: databases, queueing systems, object storage, etc. 6 So, cloud computing is now essentially a legacy enterprise service, right? Amazon Web Services introduced > 12 years ago.

Slide 7

Slide 7 text

Philipp Haller Unused potential? Cloud computing “the biggest assemblage of data capacity and distributed computing power ever available to the general public, managed as a service.” [1] 7 NO!!! The cloud is… [1] Hellerstein et al. Serverless Computing: One Step Forward, Two Steps Back. CIDR 2019 So, cloud computing is now essentially a legacy enterprise service, right?

Slide 8

Slide 8 text

Philipp Haller Function execution is autoscaling: execution scales according to demand. Users only pay for compute resources used when their code is executed. What is Serverless Computing? Functions-as-a-Service (FaaS) Developers upload their code (functions) to the cloud. No need for operating or provisioning servers. 8 Example event: “a commit was pushed to branch X of repository Y.” Pay per use! Cloud platform executes these functions in response to events. “Serverless”

Slide 9

Slide 9 text

Philipp Haller Important restrictions “Where is the catch?” Functions are stateless. Function execution duration limited. 9 Must use external storage for any data/state that needs to survive multiple function executions. AWS Lambda: all function executions must complete within 300 seconds.

Slide 10

Slide 10 text

Philipp Haller Which use cases are well-supported? What is it good for? Fully independent function invocations. Event-driven workflows connected via queueing systems or object stores. 10 Scale up or down on demand:
 “invocations never wait for each other” Depending on the patterns of function invocation [1]: “Embarrassingly parallel” High latency due to task handling and state management.

Slide 11

Slide 11 text

Philipp Haller Key limitations Communication through slow storage:
 Functions not directly network-addressable,
 all communication via external services 11 I/O bottlenecks Functions are short-lived Cannot implement general distributed systems. Cannot service repeated requests via internal caches.

Slide 12

Slide 12 text

Philipp Haller Communication latency Latency of “communicating” 1KB: 12 write+read from “long-running” function invoking a no-op Lambda function on a 1KB argument 1KB network message roundtrip [1] Hellerstein et al. Serverless Computing: One Step Forward, Two Steps Back. CIDR 2019

Slide 13

Slide 13 text

Philipp Haller Re-thinking distributed systems building Back to the roots Re-think fundamental building blocks. Devise and study programming models, languages, and systems. 13 Improve distributed systems stack. Informed by SE and systems!

Slide 14

Slide 14 text

Philipp Haller Programming model Challenge From data-shipping to function-shipping Principled fault-tolerance based on lineages. 14 Enable entirely different classes of applications:
 big data, ML model training. Guarantee properties related to fault tolerance. Example: program execution should never "get stuck" if at most N-1 out of 2N replicas fail. Requires foundations for fault-tolerant programming.

Slide 15

Slide 15 text

Philipp Haller Distributed programming with functional lineages a.k.a. function passing New data-centric programming model for functional processing of distributed data. Key ideas: 15 Provide lineages by programming abstractions Keep data stationary (if possible), send functions Utilize lineages for fault injection and recovery

Slide 16

Slide 16 text

Philipp Haller The function passing model Introducing Consists of 3 parts: Silos: stationary, typed, immutable data containers SiloRefs: references to local or remote Silos. Spores: safe, serializable functions. 16

Slide 17

Slide 17 text

Philipp Haller The function passing model Some visual intuition of Silo SiloRef Master Worker 17

Slide 18

Slide 18 text

Philipp Haller Silos What are they? Silo[T] T SiloRef[T] Two parts. def apply def send def persist def unpersist SiloRef. Handle to a Silo. Silo. Typed, stationary data container. User interacts with SiloRef. SiloRefs come with 4 primitive operations. 18

Slide 19

Slide 19 text

Philipp Haller Silos What are they? Silo[T] T SiloRef[T] Primitive: apply Takes a function that is to be applied to the data in the silo associated with the SiloRef. Creates new silo to contain the data that the user- defined function returns; evaluation is deferred def apply[S](fun: T => SiloRef[S]): SiloRef[S] Enables interesting computation DAGs Deferred def apply def send def persist def unpersist 19

Slide 20

Slide 20 text

Philipp Haller Silos What are they? Silo[T] T SiloRef[T] Primitive: send Forces the built-up computation DAG to be sent to the associated node and applied. Future is completed with the result of the computation. def send(): Future[T] EAGER def apply def send def persist def unpersist 20

Slide 21

Slide 21 text

Philipp Haller Silos Silo[T] T SiloRef[T] Silo factories: Creates silo on given host populated with given value/text file/… object SiloRef { def populate[T](host: Host, value: T): SiloRef[T] def fromTextFile(host: Host, file: File): SiloRef[List[String]] ... } def apply def send def persist def unpersist Deferred What are they? 21

Slide 22

Slide 22 text

Philipp Haller ) Basic idea: apply/send Silo[T] Machine 1 Machine 2 SiloRef[T] λ T SiloRef[S] S Silo[S] ) T㱺SiloRef[S] 22

Slide 23

Slide 23 text

Philipp Haller More involved example Silo[List[Person]] Machine 1 SiloRef[List[Person]] Let’s make an interesting DAG! Machine 2 persons: val persons: SiloRef[List[Person]] = ... 23

Slide 24

Slide 24 text

Philipp Haller More involved example Silo[List[Person]] Machine 1 SiloRef[List[Person]] Let’s make an interesting DAG! Machine 2 persons: val persons: SiloRef[List[Person]] = ... val adults = persons.apply(spore { ps => val res = ps.filter(p => p.age >= 18) SiloRef.populate(currentHost, res) }) adults 24

Slide 25

Slide 25 text

Philipp Haller More involved example Silo[List[Person]] Machine 1 SiloRef[List[Person]] Let’s make an interesting DAG! Machine 2 persons: val persons: SiloRef[List[Person]] = ... val vehicles: SiloRef[List[Vehicle]] = ... // adults that own a vehicle val owners = adults.apply(spore { val localVehicles = vehicles // spore header ps => localVehicles.apply(spore { val localps = ps // spore header vs => SiloRef.populate(currentHost, localps.flatMap(p => // list of (p, v) for a single person p vs.flatMap { v => if (v.owner.name == p.name) List((p, v)) else Nil } ) adults owners vehicles val adults = persons.apply(spore { ps => val res = ps.filter(p => p.age >= 18) SiloRef.populate(currentHost, res) }) 25

Slide 26

Slide 26 text

Philipp Haller More involved example Silo[List[Person]] Machine 1 SiloRef[List[Person]] Let’s make an interesting DAG! Machine 2 persons: val persons: SiloRef[List[Person]] = ... val vehicles: SiloRef[List[Vehicle]] = ... // adults that own a vehicle val owners = adults.apply(...) adults owners vehicles val adults = persons.apply(spore { ps => val res = ps.filter(p => p.age >= 18) SiloRef.populate(currentHost, res) }) 26

Slide 27

Slide 27 text

Philipp Haller More involved example Silo[List[Person]] Machine 1 SiloRef[List[Person]] Let’s make an interesting DAG! Machine 2 persons: val persons: SiloRef[List[Person]] = ... val vehicles: SiloRef[List[Vehicle]] = ... // adults that own a vehicle val owners = adults.apply(...) adults owners vehicles val sorted = adults.apply(spore { ps => SiloRef.populate(currentHost, ps.sortWith(p => p.age)) }) val labels = sorted.apply(spore { ps => SiloRef.populate(currentHost, ps.map(p => "Hi, " + p.name)) }) sorted labels val adults = persons.apply(spore { ps => val res = ps.filter(p => p.age >= 18) SiloRef.populate(currentHost, res) }) 27

Slide 28

Slide 28 text

Philipp Haller More involved example Silo[List[Person]] Machine 1 SiloRef[List[Person]] Let’s make an interesting DAG! Machine 2 persons: val persons: SiloRef[List[Person]] = ... val vehicles: SiloRef[List[Vehicle]] = ... // adults that own a vehicle val owners = adults.apply(...) adults owners vehicles sorted labels so far we just staged computation, we haven’t yet “kicked it off”. val adults = persons.apply(spore { ps => val res = ps.filter(p => p.age >= 18) SiloRef.populate(currentHost, res) }) val sorted = adults.apply(spore { ps => SiloRef.populate(currentHost, ps.sortWith(p => p.age)) }) val labels = sorted.apply(spore { ps => SiloRef.populate(currentHost, ps.map(p => "Hi, " + p.name)) }) 28

Slide 29

Slide 29 text

Philipp Haller More involved example Silo[List[Person]] Machine 1 SiloRef[List[Person]] Let’s make an interesting DAG! Machine 2 persons: val persons: SiloRef[List[Person]] = ... val vehicles: SiloRef[List[Vehicle]] = ... // adults that own a vehicle val owners = adults.apply(...) adults owners vehicles sorted labels λ List[Person]㱺List[String] Silo[List[String]] val adults = persons.apply(spore { ps => val res = ps.filter(p => p.age >= 18) SiloRef.populate(currentHost, res) }) val sorted = adults.apply(spore { ps => SiloRef.populate(currentHost, ps.sortWith(p => p.age)) }) val labels = sorted.apply(spore { ps => SiloRef.populate(currentHost, ps.map(p => "Hi, " + p.name)) }) labels.persist().send() 29

Slide 30

Slide 30 text

Philipp Haller A functional design for fault-tolerance A SiloRef is a lineage, a persistent (in the sense of functional programming) data structure. The lineage is the DAG of operations used to derive the data of a silo. Since the lineage is composed of spores [2], it is serializable. This means it can be persisted or transferred to other machines. Putting lineages to work 30 [2] Miller, Haller, and Odersky. Spores: a type-based foundation for closures in the age of concurrency and distribution. ECOOP '14

Slide 31

Slide 31 text

Philipp Haller Next: we formalize lineages, a concept from the database + systems communities, in the context of PL. Natural fit in context of functional programming! A functional design for fault-tolerance Putting lineages to work Formalization: typed, distributed core language with spores, silos, and futures. 31

Slide 32

Slide 32 text

Philipp Haller 32 Abstract syntax

Slide 33

Slide 33 text

Philipp Haller 33 Local reduction and lineages

Slide 34

Slide 34 text

Philipp Haller 34 Distributed reduction

Slide 35

Slide 35 text

Philipp Haller 35 Type assignment

Slide 36

Slide 36 text

Philipp Haller Properties of function passing model Formalization Subject reduction theorem guarantees preservation of types under reduction, as well as preservation of lineage mobility. Progress theorem guarantees the finite materialization of remote, lineage-based data. 36 First correctness results for a programming model for lineage-based distributed computation.

Slide 37

Slide 37 text

Philipp Haller Paper Details, proofs, etc. 37 Haller, Miller, and Müller. A Programming Model and Foundation for Lineage-Based Distributed Computation. Journal of Functional Programming 28 (2018)
 https://infoscience.epfl.ch/record/230304

Slide 38

Slide 38 text

Philipp Haller 38 Consistency, availability, partition tolerance Determinism Distributed Shared State Security & Privacy Privacy-aware distribution Information- flow security Chaos Engineering Testing hypotheses about resilience in production systems Ongoing and future work Onward

Slide 39

Slide 39 text

Philipp Haller Conclusion • Serverless computing – Promising direction, intriguing properties – Important limitations • Foundations for function-shipping – Lineage-based distributed computation – First correctness results for a programming model based on lineages • Goal: principles and foundations for a new distributed systems stack 39