SlideShare a Scribd company logo
1 of 36
Download to read offline
Taming Distribution:
Formal Protocols
for Akka Typed
Dr. Roland Kuhn
@rolandkuhn — CTO of Actyx
Distribution
=
Concurrency + Partial Failure
Distribution
=
Concurrency + Partial Failure
Distribution
=
Concurrency + Partial Failure
Actors model distribution.
Concurrency implies Nondeterminism.
Distribution implies more Nondeterminism.
Concurrency implies Nondeterminism.
Distribution implies more Nondeterminism.
Causality restricts Nondeterminism.
Some causality comes naturally.
Akka Typed Receptionist API
trait ServiceKey[T]
sealed trait Command
final case class Register[T](key: ServiceKey[T], address: ActorRef[T],
replyTo: ActorRef[Registered[T]]) extends Command
final case class Registered[T](key: ServiceKey[T], address: ActorRef[T])
final case class Find[T](key: ServiceKey[T], replyTo: ActorRef[Listing[T]]) extends Command
final case class Listing[T](key: ServiceKey[T], addresses: Set[ActorRef[T]])
… with Unregister support
trait ServiceKey[T]
sealed trait Command
final case class Register[T](key: ServiceKey[T], address: ActorRef[T],
replyTo: ActorRef[Registered[T]]) extends Command
final case class Registered[T](key: ServiceKey[T], address: ActorRef[T], handle: ActorRef[Unregister])
final case class Unregister(replyTo: ActorRef[Unregistered])
final case class Unregistered()
final case class Find[T](key: ServiceKey[T], replyTo: ActorRef[Listing[T]]) extends Command
final case class Listing[T](key: ServiceKey[T], addresses: Set[ActorRef[T]])
For everything that is not fixed by causality
coordination is needed.
Static knowledge avoids coordination.
Cluster Receptionist
Cluster Receptionist
• use FQCN of service keys as known identifier
Cluster Receptionist
• use FQCN of service keys as known identifier
• local resolution establishes static type-safety
Cluster Receptionist
• use FQCN of service keys as known identifier
• local resolution establishes static type-safety
• CRDT map from keys to sets of ActorRefs
Natural causality is not enough!
Example: payment with audit
Messages for a payment system
case object AuditService extends ServiceKey[LogActivity]
case class LogActivity(who: ActorRef[Nothing], what: String,
id: Long, replyTo: ActorRef[ActivityLogged])
case class ActivityLogged(who: ActorRef[Nothing], id: Long)
Messages for a payment system
case object AuditService extends ServiceKey[LogActivity]
case class LogActivity(who: ActorRef[Nothing], what: String,
id: Long, replyTo: ActorRef[ActivityLogged])
case class ActivityLogged(who: ActorRef[Nothing], id: Long)
sealed trait PaymentService
case class Authorize(payer: URI, amount: BigDecimal, id: UUID, replyTo: ActorRef[PaymentResult])
extends PaymentService
case class Capture(id: UUID, amount: BigDecimal, replyTo: ActorRef[PaymentResult])
extends PaymentService
case class Void(id: UUID, replyTo: ActorRef[PaymentResult]) extends PaymentService
case class Refund(id: UUID, replyTo: ActorRef[PaymentResult]) extends PaymentService
Messages for a payment system
case object AuditService extends ServiceKey[LogActivity]
case class LogActivity(who: ActorRef[Nothing], what: String,
id: Long, replyTo: ActorRef[ActivityLogged])
case class ActivityLogged(who: ActorRef[Nothing], id: Long)
sealed trait PaymentService
case class Authorize(payer: URI, amount: BigDecimal, id: UUID, replyTo: ActorRef[PaymentResult])
extends PaymentService
case class Capture(id: UUID, amount: BigDecimal, replyTo: ActorRef[PaymentResult])
extends PaymentService
case class Void(id: UUID, replyTo: ActorRef[PaymentResult]) extends PaymentService
case class Refund(id: UUID, replyTo: ActorRef[PaymentResult]) extends PaymentService
sealed trait PaymentResult
case class PaymentSuccess(id: UUID) extends PaymentResult
case class PaymentRejected(id: UUID, reason: String) extends PaymentResult
case class IdUnkwown(id: UUID) extends PaymentResult
Akka Typed crash course
case class Greet(whom: String)
class Greeter extends akka.actor.Actor {
def receive = {
case Greet(whom) => println(s"Hello $whom!")
}
}
Akka Typed crash course
case class Greet(whom: String)
class Greeter extends akka.actor.Actor {
def receive = {
case Greet(whom) => println(s"Hello $whom!")
}
}
object Greeter {
import akka.typed.scaladsl.Actor
val behavior =
Actor.immutable[Greet] { (ctx, greet) =>
println(s"Hello ${greet.whom}!")
Actor.same
}
}
First actor: do the audit
sealed trait Msg
private case object AuditDone extends Msg
private case object PaymentDone extends Msg
private def doAudit(audit: ActorRef[LogActivity], who: ActorRef[AuditDone.type], msg: String) =
Actor.deferred[ActivityLogged] { ctx =>
val id = Random.nextLong()
audit ! LogActivity(who, msg, id, ctx.self)
ctx.schedule(3.seconds, ctx.self, ActivityLogged(null, 0L))
Actor.immutable { (ctx, msg) =>
if (msg.who == null) throw new TimeoutException
else if (msg.id != id) throw new IllegalStateException
else {
who ! AuditDone
Actor.stopped
}
}
}
Second actor: do the payment
private def doPayment(from: URI, amount: BigDecimal, payments: ActorRef[PaymentService],
replyTo: ActorRef[PaymentDone.type]) =
Actor.deferred[PaymentResult] { ctx =>
val uuid = UUID.randomUUID()
payments ! Authorize(from, amount, uuid, ctx.self)
ctx.schedule(3.seconds, ctx.self, IdUnkwown(null))
Actor.immutable {
case (ctx, PaymentSuccess(`uuid`)) =>
payments ! Capture(uuid, amount, ctx.self)
Actor.immutable {
case (ctx, PaymentSuccess(`uuid`)) =>
replyTo ! PaymentDone
Actor.stopped
}
// otherwise die with MatchError
}
}
Third actor: orchestration of the process
def getMoney[R](from: URI, amount: BigDecimal,
payments: ActorRef[PaymentService], audit: ActorRef[LogActivity],
replyTo: ActorRef[R], msg: R) =
Actor.deferred[Msg] { ctx =>
ctx.watch(ctx.spawn(doAudit(audit, ctx.self, "starting payment"), "preAudit"))
Actor.immutable[Msg] {
case (ctx, AuditDone) =>
ctx.watch(ctx.spawn(doPayment(from, amount, payments, ctx.self), "payment"))
Actor.immutable[Msg] {
case (ctx, PaymentDone) =>
ctx.watch(ctx.spawn(doAudit(audit, ctx.self, "payment finished"), "postAudit"))
Actor.immutable[Msg] {
case (ctx, AuditDone) =>
replyTo ! msg
Actor.stopped
}
} onSignal terminateUponChildFailure
} onSignal terminateUponChildFailure
}
code can employ knowledge in wrong order
or
existing knowledge is not used at all
What if we prescribe effects and their order?
Which steps shall be done?
• send audit log, get confirmation for that
• send Authorize request, get confirmation
• send Capture request, get confirmation
• send audit log, get confirmation
Akka Typed Session: protocol definition
object GetMoneyProtocol extends Protocol {
type Session = //
Send[LogActivity] :: // preAudit
Read[ActivityLogged] :: //
Choice[(Halt :: _0) :+: _0 :+: CNil] :: // possibly terminate
Send[Authorize] :: // do payment
Read[PaymentResult] :: //
Choice[(Halt :: _0) :+: _0 :+: CNil] :: // possibly terminate
Send[Capture] :: //
Read[PaymentResult] :: //
Choice[(Halt :: _0) :+: _0 :+: CNil] :: // possibly terminate
Send[LogActivity] :: // postAudit
Read[ActivityLogged] :: //
Choice[(Halt :: _0) :+: _0 :+: CNil] :: // possibly terminate
_0
}
First process: do the audit
private def doAudit(audit: ActorRef[LogActivity], who: ActorRef[Nothing], msg: String) =
OpDSL[ActivityLogged] { implicit opDSL =>
val id = Random.nextLong()
for {
self <- opProcessSelf
_ <- opSend(audit, LogActivity(who, msg, id, self))
ActivityLogged(`who`, `id`) <- opRead
} yield Done
}.withTimeout(3.seconds)
/*
* the return type is
* Process[ActivityLogged, Done,
* Send[LogActivity] :: Read[ActivityLogged] ::
* Choice[(Halt :: _0) :+: _0 :+: CNil] :: _0]
*/
Second process: do the payment
private def doPayment(from: URI, amount: BigDecimal, payments: ActorRef[PaymentService]) =
OpDSL[PaymentResult] { implicit opDSL =>
val uuid = UUID.randomUUID()
for {
self <- opProcessSelf
_ <- opSend(payments, Authorize(from, amount, uuid, self))
PaymentSuccess(`uuid`) <- opRead
_ <- opSend(payments, Capture(uuid, amount, self))
PaymentSuccess(`uuid`) <- opRead
} yield Done
}.withTimeout(3.seconds)
/*
* the return type is
* Process[PaymentResult, Done,
* Send[Authorize] :: Read[PaymentResult] ::
* Choice[(Halt :: _0) :+: _0 :+: CNil] ::
* Send[Capture] :: Read[PaymentResult] ::
* Choice[(Halt :: _0) :+: _0 :+: CNil] :: _0]
*/
Third process: orchestrate and verify
// this useful process is provided by the Session DSL and talks to the Receptionist
def getService[T](key: ServiceKey[T]): Operation[Listing[T], ActorRef[T], _0]
def getMoney[R](from: URI, amount: BigDecimal,
payments: ActorRef[PaymentService],
replyTo: ActorRef[R], msg: R) =
OpDSL[Nothing] { implicit opDSL =>
for {
self <- opProcessSelf
audit <- opCall(getService(AuditService) .named("getAuditService"))
_ <- opCall(doAudit(audit, self, "starting payment").named("preAudit"))
_ <- opCall(doPayment(from, amount, payments) .named("payment"))
_ <- opCall(doAudit(audit, self, "payment finished").named("postAudit"))
} yield replyTo ! msg
}
Third process: orchestrate and verify
// this useful process is provided by the Session DSL and talks to the Receptionist
def getService[T](key: ServiceKey[T]): Operation[Listing[T], ActorRef[T], _0]
def getMoney[R](from: URI, amount: BigDecimal,
payments: ActorRef[PaymentService],
replyTo: ActorRef[R], msg: R) =
OpDSL[Nothing] { implicit opDSL =>
for {
self <- opProcessSelf
audit <- opCall(getService(AuditService) .named("getAuditService"))
_ <- opCall(doAudit(audit, self, "starting payment").named("preAudit"))
_ <- opCall(doPayment(from, amount, payments) .named("payment"))
_ <- opCall(doAudit(audit, self, "payment finished").named("postAudit"))
} yield replyTo ! msg
}
// compile-time verification (TODO: should be a macro)
private def verify = E.vetExternalProtocol(GetMoneyProtocol, getMoney(???, ???, ???, ???, ???))
Conclusion:
There is a lot on the table,
get involved!
https://github.com/rkuhn/akka-typed-session

More Related Content

What's hot

Groovy Api Tutorial
Groovy Api  TutorialGroovy Api  Tutorial
Groovy Api Tutorial
guligala
 
The Logical Burrito - pattern matching, term rewriting and unification
The Logical Burrito - pattern matching, term rewriting and unificationThe Logical Burrito - pattern matching, term rewriting and unification
The Logical Burrito - pattern matching, term rewriting and unification
Norman Richards
 
Introduction to Gremlin
Introduction to GremlinIntroduction to Gremlin
Introduction to Gremlin
Max De Marzi
 

What's hot (16)

Groovy Api Tutorial
Groovy Api  TutorialGroovy Api  Tutorial
Groovy Api Tutorial
 
つくってあそぼ Kotlin DSL ~拡張編~
つくってあそぼ Kotlin DSL ~拡張編~つくってあそぼ Kotlin DSL ~拡張編~
つくってあそぼ Kotlin DSL ~拡張編~
 
つくってあそぼ Kotlin DSL 第2版
つくってあそぼ Kotlin DSL 第2版つくってあそぼ Kotlin DSL 第2版
つくってあそぼ Kotlin DSL 第2版
 
Introduction to kotlin + spring boot demo
Introduction to kotlin + spring boot demoIntroduction to kotlin + spring boot demo
Introduction to kotlin + spring boot demo
 
Groovy grails types, operators, objects
Groovy grails types, operators, objectsGroovy grails types, operators, objects
Groovy grails types, operators, objects
 
core.logic introduction
core.logic introductioncore.logic introduction
core.logic introduction
 
The Ring programming language version 1.6 book - Part 40 of 189
The Ring programming language version 1.6 book - Part 40 of 189The Ring programming language version 1.6 book - Part 40 of 189
The Ring programming language version 1.6 book - Part 40 of 189
 
Apache Spark - Key Value RDD - Transformations | Big Data Hadoop Spark Tutori...
Apache Spark - Key Value RDD - Transformations | Big Data Hadoop Spark Tutori...Apache Spark - Key Value RDD - Transformations | Big Data Hadoop Spark Tutori...
Apache Spark - Key Value RDD - Transformations | Big Data Hadoop Spark Tutori...
 
Greach 2015 AST – Groovy Transformers: More than meets the eye!
Greach 2015   AST – Groovy Transformers: More than meets the eye!Greach 2015   AST – Groovy Transformers: More than meets the eye!
Greach 2015 AST – Groovy Transformers: More than meets the eye!
 
TDC218SP | Trilha Kotlin - DSLs in a Kotlin Way
TDC218SP | Trilha Kotlin - DSLs in a Kotlin WayTDC218SP | Trilha Kotlin - DSLs in a Kotlin Way
TDC218SP | Trilha Kotlin - DSLs in a Kotlin Way
 
Pragmatic Real-World Scala (short version)
Pragmatic Real-World Scala (short version)Pragmatic Real-World Scala (short version)
Pragmatic Real-World Scala (short version)
 
Scala
ScalaScala
Scala
 
The Logical Burrito - pattern matching, term rewriting and unification
The Logical Burrito - pattern matching, term rewriting and unificationThe Logical Burrito - pattern matching, term rewriting and unification
The Logical Burrito - pattern matching, term rewriting and unification
 
Sneaking inside Kotlin features
Sneaking inside Kotlin featuresSneaking inside Kotlin features
Sneaking inside Kotlin features
 
Introduction to Gremlin
Introduction to GremlinIntroduction to Gremlin
Introduction to Gremlin
 
Kotlin Advanced - Apalon Kotlin Sprint Part 3
Kotlin Advanced - Apalon Kotlin Sprint Part 3Kotlin Advanced - Apalon Kotlin Sprint Part 3
Kotlin Advanced - Apalon Kotlin Sprint Part 3
 

Similar to Taming Distribution: Formal Protocols for Akka Typed

i need to write a return type function that takes information about th.docx
i need to write a return type function that takes information about th.docxi need to write a return type function that takes information about th.docx
i need to write a return type function that takes information about th.docx
hendriciraida
 
i need to modify this c++ so when the user enter services thats not an.docx
i need to modify this c++ so when the user enter services thats not an.docxi need to modify this c++ so when the user enter services thats not an.docx
i need to modify this c++ so when the user enter services thats not an.docx
hendriciraida
 
Building @Anywhere (for TXJS)
Building @Anywhere (for TXJS)Building @Anywhere (for TXJS)
Building @Anywhere (for TXJS)
danwrong
 
Introducing the WSO2 Complex Event Processor
Introducing the WSO2 Complex Event ProcessorIntroducing the WSO2 Complex Event Processor
Introducing the WSO2 Complex Event Processor
WSO2
 
Rajeev oops 2nd march
Rajeev oops 2nd marchRajeev oops 2nd march
Rajeev oops 2nd march
Rajeev Sharan
 
i want to add to this c++ code a condition so that you can only chose.docx
i want to add to this c++ code a condition so that you can only chose.docxi want to add to this c++ code a condition so that you can only chose.docx
i want to add to this c++ code a condition so that you can only chose.docx
hendriciraida
 
Coordinating non blocking io melb-clj
Coordinating non blocking io melb-cljCoordinating non blocking io melb-clj
Coordinating non blocking io melb-clj
Logan Campbell
 
Open XKE - POC d'une architecture distribuée de calculs financiers par Xavier...
Open XKE - POC d'une architecture distribuée de calculs financiers par Xavier...Open XKE - POC d'une architecture distribuée de calculs financiers par Xavier...
Open XKE - POC d'une architecture distribuée de calculs financiers par Xavier...
Publicis Sapient Engineering
 

Similar to Taming Distribution: Formal Protocols for Akka Typed (20)

i need to write a return type function that takes information about th.docx
i need to write a return type function that takes information about th.docxi need to write a return type function that takes information about th.docx
i need to write a return type function that takes information about th.docx
 
i need to modify this c++ so when the user enter services thats not an.docx
i need to modify this c++ so when the user enter services thats not an.docxi need to modify this c++ so when the user enter services thats not an.docx
i need to modify this c++ so when the user enter services thats not an.docx
 
A Series of Fortunate Events - Drupalcon Europe, Amsterdam 2014
A Series of Fortunate Events - Drupalcon Europe, Amsterdam 2014A Series of Fortunate Events - Drupalcon Europe, Amsterdam 2014
A Series of Fortunate Events - Drupalcon Europe, Amsterdam 2014
 
Powering Heap With PostgreSQL And CitusDB (PGConf Silicon Valley 2015)
Powering Heap With PostgreSQL And CitusDB (PGConf Silicon Valley 2015)Powering Heap With PostgreSQL And CitusDB (PGConf Silicon Valley 2015)
Powering Heap With PostgreSQL And CitusDB (PGConf Silicon Valley 2015)
 
Creating an Uber Clone - Part XXXII.pdf
Creating an Uber Clone - Part XXXII.pdfCreating an Uber Clone - Part XXXII.pdf
Creating an Uber Clone - Part XXXII.pdf
 
Building @Anywhere (for TXJS)
Building @Anywhere (for TXJS)Building @Anywhere (for TXJS)
Building @Anywhere (for TXJS)
 
Introducing the WSO2 Complex Event Processor
Introducing the WSO2 Complex Event ProcessorIntroducing the WSO2 Complex Event Processor
Introducing the WSO2 Complex Event Processor
 
Serverless stateful
Serverless statefulServerless stateful
Serverless stateful
 
CAVE Overview
CAVE OverviewCAVE Overview
CAVE Overview
 
Improving Correctness With Type - Goto Con Berlin
Improving Correctness With Type - Goto Con BerlinImproving Correctness With Type - Goto Con Berlin
Improving Correctness With Type - Goto Con Berlin
 
Rxjs swetugg
Rxjs swetuggRxjs swetugg
Rxjs swetugg
 
Rajeev oops 2nd march
Rajeev oops 2nd marchRajeev oops 2nd march
Rajeev oops 2nd march
 
i want to add to this c++ code a condition so that you can only chose.docx
i want to add to this c++ code a condition so that you can only chose.docxi want to add to this c++ code a condition so that you can only chose.docx
i want to add to this c++ code a condition so that you can only chose.docx
 
ZIO actors by Mateusz Sokół Scalac
ZIO actors by Mateusz Sokół ScalacZIO actors by Mateusz Sokół Scalac
ZIO actors by Mateusz Sokół Scalac
 
Serverless Stateful Architecture
Serverless Stateful ArchitectureServerless Stateful Architecture
Serverless Stateful Architecture
 
Coordinating non blocking io melb-clj
Coordinating non blocking io melb-cljCoordinating non blocking io melb-clj
Coordinating non blocking io melb-clj
 
Small pieces loosely joined
Small pieces loosely joinedSmall pieces loosely joined
Small pieces loosely joined
 
Functional Principles for OO Developers
Functional Principles for OO DevelopersFunctional Principles for OO Developers
Functional Principles for OO Developers
 
Creating a Facebook Clone - Part XXVI - Transcript.pdf
Creating a Facebook Clone - Part XXVI - Transcript.pdfCreating a Facebook Clone - Part XXVI - Transcript.pdf
Creating a Facebook Clone - Part XXVI - Transcript.pdf
 
Open XKE - POC d'une architecture distribuée de calculs financiers par Xavier...
Open XKE - POC d'une architecture distribuée de calculs financiers par Xavier...Open XKE - POC d'une architecture distribuée de calculs financiers par Xavier...
Open XKE - POC d'une architecture distribuée de calculs financiers par Xavier...
 

More from Roland Kuhn

Akka cluster overview at 010dev
Akka cluster overview at 010devAkka cluster overview at 010dev
Akka cluster overview at 010dev
Roland Kuhn
 

More from Roland Kuhn (9)

Distributed systems vs compositionality
Distributed systems vs compositionalityDistributed systems vs compositionality
Distributed systems vs compositionality
 
Reactive Design Patterns — J on the Beach
Reactive Design Patterns — J on the BeachReactive Design Patterns — J on the Beach
Reactive Design Patterns — J on the Beach
 
The Newest in Session Types
The Newest in Session TypesThe Newest in Session Types
The Newest in Session Types
 
Akka Streams and HTTP
Akka Streams and HTTPAkka Streams and HTTP
Akka Streams and HTTP
 
Akka and AngularJS – Reactive Applications in Practice
Akka and AngularJS – Reactive Applications in PracticeAkka and AngularJS – Reactive Applications in Practice
Akka and AngularJS – Reactive Applications in Practice
 
Go Reactive: Blueprint for Future Applications
Go Reactive: Blueprint for Future ApplicationsGo Reactive: Blueprint for Future Applications
Go Reactive: Blueprint for Future Applications
 
Reactive Streams: Handling Data-Flow the Reactive Way
Reactive Streams: Handling Data-Flow the Reactive WayReactive Streams: Handling Data-Flow the Reactive Way
Reactive Streams: Handling Data-Flow the Reactive Way
 
Akka cluster overview at 010dev
Akka cluster overview at 010devAkka cluster overview at 010dev
Akka cluster overview at 010dev
 
Akka typed-channels
Akka typed-channelsAkka typed-channels
Akka typed-channels
 

Recently uploaded

Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Safe Software
 

Recently uploaded (20)

Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
 
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ..."I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...
 
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
 
Real Time Object Detection Using Open CV
Real Time Object Detection Using Open CVReal Time Object Detection Using Open CV
Real Time Object Detection Using Open CV
 
ICT role in 21st century education and its challenges
ICT role in 21st century education and its challengesICT role in 21st century education and its challenges
ICT role in 21st century education and its challenges
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?
 
Manulife - Insurer Transformation Award 2024
Manulife - Insurer Transformation Award 2024Manulife - Insurer Transformation Award 2024
Manulife - Insurer Transformation Award 2024
 
Strategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherStrategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a Fresher
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
 
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
 
Apidays Singapore 2024 - Modernizing Securities Finance by Madhu Subbu
Apidays Singapore 2024 - Modernizing Securities Finance by Madhu SubbuApidays Singapore 2024 - Modernizing Securities Finance by Madhu Subbu
Apidays Singapore 2024 - Modernizing Securities Finance by Madhu Subbu
 
Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...
 
MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024
 
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWEREMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processors
 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
 

Taming Distribution: Formal Protocols for Akka Typed

  • 1. Taming Distribution: Formal Protocols for Akka Typed Dr. Roland Kuhn @rolandkuhn — CTO of Actyx
  • 9. Some causality comes naturally.
  • 10. Akka Typed Receptionist API trait ServiceKey[T] sealed trait Command final case class Register[T](key: ServiceKey[T], address: ActorRef[T], replyTo: ActorRef[Registered[T]]) extends Command final case class Registered[T](key: ServiceKey[T], address: ActorRef[T]) final case class Find[T](key: ServiceKey[T], replyTo: ActorRef[Listing[T]]) extends Command final case class Listing[T](key: ServiceKey[T], addresses: Set[ActorRef[T]])
  • 11. … with Unregister support trait ServiceKey[T] sealed trait Command final case class Register[T](key: ServiceKey[T], address: ActorRef[T], replyTo: ActorRef[Registered[T]]) extends Command final case class Registered[T](key: ServiceKey[T], address: ActorRef[T], handle: ActorRef[Unregister]) final case class Unregister(replyTo: ActorRef[Unregistered]) final case class Unregistered() final case class Find[T](key: ServiceKey[T], replyTo: ActorRef[Listing[T]]) extends Command final case class Listing[T](key: ServiceKey[T], addresses: Set[ActorRef[T]])
  • 12. For everything that is not fixed by causality coordination is needed.
  • 13. Static knowledge avoids coordination.
  • 15. Cluster Receptionist • use FQCN of service keys as known identifier
  • 16. Cluster Receptionist • use FQCN of service keys as known identifier • local resolution establishes static type-safety
  • 17. Cluster Receptionist • use FQCN of service keys as known identifier • local resolution establishes static type-safety • CRDT map from keys to sets of ActorRefs
  • 18. Natural causality is not enough!
  • 20. Messages for a payment system case object AuditService extends ServiceKey[LogActivity] case class LogActivity(who: ActorRef[Nothing], what: String, id: Long, replyTo: ActorRef[ActivityLogged]) case class ActivityLogged(who: ActorRef[Nothing], id: Long)
  • 21. Messages for a payment system case object AuditService extends ServiceKey[LogActivity] case class LogActivity(who: ActorRef[Nothing], what: String, id: Long, replyTo: ActorRef[ActivityLogged]) case class ActivityLogged(who: ActorRef[Nothing], id: Long) sealed trait PaymentService case class Authorize(payer: URI, amount: BigDecimal, id: UUID, replyTo: ActorRef[PaymentResult]) extends PaymentService case class Capture(id: UUID, amount: BigDecimal, replyTo: ActorRef[PaymentResult]) extends PaymentService case class Void(id: UUID, replyTo: ActorRef[PaymentResult]) extends PaymentService case class Refund(id: UUID, replyTo: ActorRef[PaymentResult]) extends PaymentService
  • 22. Messages for a payment system case object AuditService extends ServiceKey[LogActivity] case class LogActivity(who: ActorRef[Nothing], what: String, id: Long, replyTo: ActorRef[ActivityLogged]) case class ActivityLogged(who: ActorRef[Nothing], id: Long) sealed trait PaymentService case class Authorize(payer: URI, amount: BigDecimal, id: UUID, replyTo: ActorRef[PaymentResult]) extends PaymentService case class Capture(id: UUID, amount: BigDecimal, replyTo: ActorRef[PaymentResult]) extends PaymentService case class Void(id: UUID, replyTo: ActorRef[PaymentResult]) extends PaymentService case class Refund(id: UUID, replyTo: ActorRef[PaymentResult]) extends PaymentService sealed trait PaymentResult case class PaymentSuccess(id: UUID) extends PaymentResult case class PaymentRejected(id: UUID, reason: String) extends PaymentResult case class IdUnkwown(id: UUID) extends PaymentResult
  • 23. Akka Typed crash course case class Greet(whom: String) class Greeter extends akka.actor.Actor { def receive = { case Greet(whom) => println(s"Hello $whom!") } }
  • 24. Akka Typed crash course case class Greet(whom: String) class Greeter extends akka.actor.Actor { def receive = { case Greet(whom) => println(s"Hello $whom!") } } object Greeter { import akka.typed.scaladsl.Actor val behavior = Actor.immutable[Greet] { (ctx, greet) => println(s"Hello ${greet.whom}!") Actor.same } }
  • 25. First actor: do the audit sealed trait Msg private case object AuditDone extends Msg private case object PaymentDone extends Msg private def doAudit(audit: ActorRef[LogActivity], who: ActorRef[AuditDone.type], msg: String) = Actor.deferred[ActivityLogged] { ctx => val id = Random.nextLong() audit ! LogActivity(who, msg, id, ctx.self) ctx.schedule(3.seconds, ctx.self, ActivityLogged(null, 0L)) Actor.immutable { (ctx, msg) => if (msg.who == null) throw new TimeoutException else if (msg.id != id) throw new IllegalStateException else { who ! AuditDone Actor.stopped } } }
  • 26. Second actor: do the payment private def doPayment(from: URI, amount: BigDecimal, payments: ActorRef[PaymentService], replyTo: ActorRef[PaymentDone.type]) = Actor.deferred[PaymentResult] { ctx => val uuid = UUID.randomUUID() payments ! Authorize(from, amount, uuid, ctx.self) ctx.schedule(3.seconds, ctx.self, IdUnkwown(null)) Actor.immutable { case (ctx, PaymentSuccess(`uuid`)) => payments ! Capture(uuid, amount, ctx.self) Actor.immutable { case (ctx, PaymentSuccess(`uuid`)) => replyTo ! PaymentDone Actor.stopped } // otherwise die with MatchError } }
  • 27. Third actor: orchestration of the process def getMoney[R](from: URI, amount: BigDecimal, payments: ActorRef[PaymentService], audit: ActorRef[LogActivity], replyTo: ActorRef[R], msg: R) = Actor.deferred[Msg] { ctx => ctx.watch(ctx.spawn(doAudit(audit, ctx.self, "starting payment"), "preAudit")) Actor.immutable[Msg] { case (ctx, AuditDone) => ctx.watch(ctx.spawn(doPayment(from, amount, payments, ctx.self), "payment")) Actor.immutable[Msg] { case (ctx, PaymentDone) => ctx.watch(ctx.spawn(doAudit(audit, ctx.self, "payment finished"), "postAudit")) Actor.immutable[Msg] { case (ctx, AuditDone) => replyTo ! msg Actor.stopped } } onSignal terminateUponChildFailure } onSignal terminateUponChildFailure }
  • 28. code can employ knowledge in wrong order or existing knowledge is not used at all
  • 29. What if we prescribe effects and their order?
  • 30. Which steps shall be done? • send audit log, get confirmation for that • send Authorize request, get confirmation • send Capture request, get confirmation • send audit log, get confirmation
  • 31. Akka Typed Session: protocol definition object GetMoneyProtocol extends Protocol { type Session = // Send[LogActivity] :: // preAudit Read[ActivityLogged] :: // Choice[(Halt :: _0) :+: _0 :+: CNil] :: // possibly terminate Send[Authorize] :: // do payment Read[PaymentResult] :: // Choice[(Halt :: _0) :+: _0 :+: CNil] :: // possibly terminate Send[Capture] :: // Read[PaymentResult] :: // Choice[(Halt :: _0) :+: _0 :+: CNil] :: // possibly terminate Send[LogActivity] :: // postAudit Read[ActivityLogged] :: // Choice[(Halt :: _0) :+: _0 :+: CNil] :: // possibly terminate _0 }
  • 32. First process: do the audit private def doAudit(audit: ActorRef[LogActivity], who: ActorRef[Nothing], msg: String) = OpDSL[ActivityLogged] { implicit opDSL => val id = Random.nextLong() for { self <- opProcessSelf _ <- opSend(audit, LogActivity(who, msg, id, self)) ActivityLogged(`who`, `id`) <- opRead } yield Done }.withTimeout(3.seconds) /* * the return type is * Process[ActivityLogged, Done, * Send[LogActivity] :: Read[ActivityLogged] :: * Choice[(Halt :: _0) :+: _0 :+: CNil] :: _0] */
  • 33. Second process: do the payment private def doPayment(from: URI, amount: BigDecimal, payments: ActorRef[PaymentService]) = OpDSL[PaymentResult] { implicit opDSL => val uuid = UUID.randomUUID() for { self <- opProcessSelf _ <- opSend(payments, Authorize(from, amount, uuid, self)) PaymentSuccess(`uuid`) <- opRead _ <- opSend(payments, Capture(uuid, amount, self)) PaymentSuccess(`uuid`) <- opRead } yield Done }.withTimeout(3.seconds) /* * the return type is * Process[PaymentResult, Done, * Send[Authorize] :: Read[PaymentResult] :: * Choice[(Halt :: _0) :+: _0 :+: CNil] :: * Send[Capture] :: Read[PaymentResult] :: * Choice[(Halt :: _0) :+: _0 :+: CNil] :: _0] */
  • 34. Third process: orchestrate and verify // this useful process is provided by the Session DSL and talks to the Receptionist def getService[T](key: ServiceKey[T]): Operation[Listing[T], ActorRef[T], _0] def getMoney[R](from: URI, amount: BigDecimal, payments: ActorRef[PaymentService], replyTo: ActorRef[R], msg: R) = OpDSL[Nothing] { implicit opDSL => for { self <- opProcessSelf audit <- opCall(getService(AuditService) .named("getAuditService")) _ <- opCall(doAudit(audit, self, "starting payment").named("preAudit")) _ <- opCall(doPayment(from, amount, payments) .named("payment")) _ <- opCall(doAudit(audit, self, "payment finished").named("postAudit")) } yield replyTo ! msg }
  • 35. Third process: orchestrate and verify // this useful process is provided by the Session DSL and talks to the Receptionist def getService[T](key: ServiceKey[T]): Operation[Listing[T], ActorRef[T], _0] def getMoney[R](from: URI, amount: BigDecimal, payments: ActorRef[PaymentService], replyTo: ActorRef[R], msg: R) = OpDSL[Nothing] { implicit opDSL => for { self <- opProcessSelf audit <- opCall(getService(AuditService) .named("getAuditService")) _ <- opCall(doAudit(audit, self, "starting payment").named("preAudit")) _ <- opCall(doPayment(from, amount, payments) .named("payment")) _ <- opCall(doAudit(audit, self, "payment finished").named("postAudit")) } yield replyTo ! msg } // compile-time verification (TODO: should be a macro) private def verify = E.vetExternalProtocol(GetMoneyProtocol, getMoney(???, ???, ???, ???, ???))
  • 36. Conclusion: There is a lot on the table, get involved! https://github.com/rkuhn/akka-typed-session