DDDing Tools = Akka Persistence
Upcoming SlideShare
Loading in...5
×
 

DDDing Tools = Akka Persistence

on

  • 2,685 views

Event sourcing and Domain Driven Design are techniques that allow you to model your business more truthfully - by expressing it via commands, events and aggregates etc. The new akka-persistence ...

Event sourcing and Domain Driven Design are techniques that allow you to model your business more truthfully - by expressing it via commands, events and aggregates etc. The new akka-persistence module, included in Akka since the 2.3 release is aimed at easing implementing event sourced applications. Turns out the actor model and events as messages fit in here perfectly. During this session we'll discover how to build reactive, event sourcing based apps using the new abstractions provided, and investigate how to implement your own journals to back these persistent event sourced actors.

Statistics

Views

Total Views
2,685
Views on SlideShare
2,183
Embed Views
502

Actions

Likes
12
Downloads
69
Comments
1

3 Embeds 502

https://twitter.com 493
http://www.slideee.com 5
http://www.conferize.com 4

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

CC Attribution-NonCommercial-ShareAlike LicenseCC Attribution-NonCommercial-ShareAlike LicenseCC Attribution-NonCommercial-ShareAlike License

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
  • Warning, the here presented APIs are from 2.3.3, and with this being an EXPERIMENTAL module, we have changed them slightly for 2.3.4. These are expected to remain stable. Please check the docs before writing code :-)
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

DDDing Tools = Akka Persistence DDDing Tools = Akka Persistence Presentation Transcript

  • DDDing with Akka Persistence ! Konrad 'ktoso' Malawski GeeCON 2014 @ Kraków, PL Konrad `@ktosopl` Malawski
  • Konrad `@ktosopl` Malawski hAkker @
  • Konrad `@ktosopl` Malawski typesafe.com geecon.org Java.pl / KrakowScala.pl sckrk.com / meetup.com/Paper-Cup @ London GDGKrakow.pl meetup.com/Lambda-Lounge-Krakow hAkker @
  • mainly by Martin Krasser ! ! (as contractor for Typesafe) ! inspired by: https://github.com/krasserm https://github.com/eligosource/eventsourced akka-persistence
  • Dependencies libraryDependencies ++= Seq(! "com.typesafe.akka" %% “akka-actor" % "2.3.1",! "com.typesafe.akka" %% "akka-persistence-experimental" % "2.3.1"! )
  • Show of hands!
  • Show of hands!
  • Show of hands!
  • Show of hands!
  • sourcing styles Command Sourcing Event Sourcing msg: DoThing msg persisted before receive imperative, “do the thing” business logic change, can be reflected in reaction Processor
  • sourcing styles Command Sourcing Event Sourcing msg: DoThing msg: ThingDone msg persisted before receive commands converted to events, must be manually persisted imperative, “do the thing” past tense, “happened” business logic change, can be reflected in reaction business logic change, won’t change previous events Processor EventsourcedProcessor
  • Compared to “Good ol’ CRUD Model” state “Mutable Record” state = apply(es) “Series of Events”
  • Actors
  • count: 0 ! ! Actor An Actor that keeps count of messages it processed ! Let’s send 2 messages to it (it’s “commands”)
  • Actor ! ! class Counter extends Actor {! var count = 0! 
 def receive = {! case _ => count += 1! }! }
  • count: 0 ! ! Actor
  • count: 0 ! ! Actor
  • count: 1 ! ! Actor crash!
  • Actor crash!
  • Actor restart
  • count: 0 ! ! Actor restart
  • count: 0 ! ! Actor restarted
  • count: 1 ! ! Actor restarted
  • count: 1 ! ! wrong! expected count == 2! Actor restarted
  • Consistency Boundary
  • Consistency Boundary equals Async Boundary
  • Boundaries actor actor async
  • Boundaries aggregate aggregate “eventual” A.K.A. async
  • Business rules aggregate Any rule that spans AGGREGATES will not be expected to be up- to-date at all times.Through event processing, batch processing, or other update mechanisms, other dependencies can be resolved within some specific time. [Evans, p. 128] aggregate consistent consistent eventually consistent
  • Let’s open the toolbox
  • Processor
  • Processor (command sourcing)
  • var count = 0 ! def processorId = “a” ! Journal (DB) ! ! ! Processor
  • Journal (DB) ! ! ! Processor var count = 0 ! def processorId = “a” !
  • Journal (DB) ! ! ! Processor var count = 0 ! def processorId = “a” !
  • Journal (DB) ! ! ! Processor var count = 0 ! def processorId = “a” !
  • Journal (DB) ! ! ! Processor var count = 1 ! def processorId = “a” !
  • Journal (DB) ! ! ! Processor var count = 1 ! def processorId = “a” ! crash!
  • Journal (DB) ! ! ! Processor restart
  • Journal (DB) ! ! ! Processor var count = 0 ! def processorId = “a” ! restart
  • Journal (DB) ! ! ! Processor var count = 0 ! def processorId = “a” ! replay! restart
  • Journal (DB) ! ! ! Processor var count = 0 ! def processorId = “a” ! replay!
  • Journal (DB) ! ! ! Processor var count = 1 ! def processorId = “a” ! replay!
  • Journal (DB) ! ! ! Processor var count = 1 ! def processorId = “a” !
  • Journal (DB) ! ! ! Processor var count = 2 ! def processorId = “a” ! yay!
  • Processor import akka.persistence._! ! class CounterProcessor extends Processor {! var count = 0! 
 override val processorId = "counter"! ! def receive = {! case Persistent(payload, seqNr) =>! // payload already persisted! count += 1! }! }
  • Processor import akka.persistence._! ! class CounterProcessor extends Processor {! var count = 0! 
 override val processorId = "counter"! ! def receive = {! case Persistent(payload, seqNr) =>! // payload already persisted! count += 1! }! } counter ! Persistent(payload)
  • Processor import akka.persistence._! ! class CounterProcessor extends Processor {! var count = 0! 
 override val processorId = "counter"! ! def receive = {! case Persistent(payload, seqNr) =>! // payload already persisted! count += 1! }! } counter ! Persistent(payload)
  • Processor import akka.persistence._! ! class CounterProcessor extends Processor {! var count = 0! 
 override val processorId = "counter"! ! def receive = {! case Persistent(payload, seqNr) =>! // payload already persisted! count += 1! }! } counter ! Persistent(payload) sequenceNr (generated by akka) is already persisted!
  • Processor import akka.persistence._! ! class CounterProcessor extends Processor {! var count = 0! 
 override val processorId = "counter"! ! def receive = {! case notPersisted: Event =>! // will not replay this msg!! count += 1! }! } counter ! payload won’t persist won’t replay
  • Processor import akka.persistence._! ! class CounterProcessor extends Processor {! var count = 0! 
 override val processorId = "counter"! ! def receive = {! case Persistent(payload, seqNr) =>! // payload already persisted! count += 1! ! case notPersistentMsg =>! // msg not persisted - like in normal Actor! count += 1! }! }
  • Processor Upsides • Persistent Command Sourcing “out of the box” • Pretty simple, persist handled for you • Once you get the msg, it’s persisted • Pluggable Journals (HBase, Cassandra, Mongo, …) • Can replay to given seqNr (post-mortem etc!)
  • Processor Downsides • Exposes Persistent() to Actors who talk to you • No room for validation before persisting • There’s one Model, we act on the incoming msg • Lower throughput than plain Actor (limited by DB)
  • Eventsourced Processor
  • Eventsourced Processor (event sourcing)
  • super quick domain modeling! sealed trait Command! case class GiveMe(geeCoins: Int) extends Command! case class TakeMy(geeCoins: Int) extends Command Commands - what others “tell” us; not persisted case class Wallet(geeCoins: Int) {! def updated(diff: Int) = State(geeCoins + diff)! } State - reflection of a series of events sealed trait Event! case class BalanceChangedBy(geeCoins: Int) extends Event! Events - reflect effects, past tense; persisted
  • var state = S0 ! def processorId = “a” ! EventsourcedProcessor Command ! ! Journal
  • EventsourcedProcessor var state = S0 ! def processorId = “a” ! ! ! Journal Generate Events
  • EventsourcedProcessor var state = S0 ! def processorId = “a” ! ! ! Journal Generate Events E1
  • EventsourcedProcessor ACK “persisted” ! ! Journal E1 var state = S0 ! def processorId = “a” !
  • EventsourcedProcessor “Apply” event ! ! Journal E1 var state = S0 ! def processorId = “a” ! E1
  • EventsourcedProcessor ! ! Journal E1 var state = S0 ! def processorId = “a” ! E1 Okey!
  • EventsourcedProcessor ! ! Journal E1 var state = S0 ! def processorId = “a” ! E1 Okey!
  • EventsourcedProcessor ! ! Journal E1 var state = S0 ! def processorId = “a” ! E1 Ok, he got my $.
  • EventsourcedProcessor class GeeCoinWallet extends EventsourcedProcessor {! ! var state = Wallet(geeCoins = 0)! ! def updateState(e: Event): State = {! case BalanceChangedBy(geeCoins) => state updated geeCoins! }! ! // API:! ! def receiveCommand = ??? // TODO! ! def receiveRecover = ??? // TODO! ! }!
  • EventsourcedProcessor def receiveCommand = {! ! case TakeMy(geeCoins) =>! persist(BalanceChangedBy(geeCoins)) { changed =>! state = updateState(changed) ! }! ! ! ! ! ! ! } async callback
  • EventsourcedProcessor def receiveCommand = {! ! ! ! ! ! ! case GiveMe(geeCoins) if geeCoins <= state.geeCoins =>! persist(BalanceChangedBy(-geeCoins)) { changed =>! state = updateState(changed) ! sender() ! TakeMy(-geeCoins)! }! } Safe to access sender here async callback
  • EventsourcedProcessor def receiveCommand = {! ! ! ! ! ! ! case GiveMe(geeCoins) if geeCoins <= state.geeCoins =>! persist(BalanceChangedBy(-geeCoins)) { changed =>! state = updateState(changed) ! sender() ! TakeMy(-geeCoins)! }! } Is this safe? It’s async! Races?!
  • Ordering guarantees ! ! Journal E1 var state = S0 ! def processorId = “a” ! C1 C2 C3
  • Ordering guarantees ! ! Journal E1 var state = S0 ! def processorId = “a” ! C1 C2 C3 Commands get “stashed” until processing C1’s events are acted upon.
  • ! ! Journal Ordering guarantees var state = S0 ! def processorId = “a” ! C1 C2 C3 E1 E2 E2E1 events get applied in-order
  • C2 ! ! Journal Ordering guarantees var state = S0 ! def processorId = “a” ! C3 E1 E2 E2E1 and the cycle repeats
  • Eventsourced, recovery /** MUST NOT SIDE-EFFECT! */! def receiveRecover = {! case replayedEvent: Event => ! updateState(replayedEvent)! } exact same code for all events!
  • Snapshots
  • Snapshots (in SnapshotStore)
  • Eventsourced, snapshots def receiveCommand = {! case command: Command =>! saveSnapshot(state) // async!! } /** MUST NOT SIDE-EFFECT! */! def receiveRecover = {! case SnapshotOffer(meta, snapshot: State) => ! this.state = state! ! case replayedEvent: Event => ! updateState(replayedEvent)! } snapshot!? how?
  • …sum of states… Snapshots ! ! Journal E1 E2 E3 E4 E5 E6 E7 E8
  • state until [E8] Snapshots S8 ! ! Snapshot Store snapshot! ! ! Journal E1 E2 E3 E4 E5 E6 E7 E8
  • state until [E8] Snapshots S8 ! ! Snapshot Store ! ! Journal E1 E2 E3 E4 E5 E6 E7 E8 S8 crash!
  • Snapshots ! ! Snapshot Store ! ! Journal E1 E2 E3 E4 E5 E6 E7 E8 S8 crash!
  • “bring me up-to-date!” Snapshots ! ! Snapshot Store ! ! Journal E1 E2 E3 E4 E5 E6 E7 E8 S8 restart! replay!
  • “bring me up-to-date!” Snapshots ! ! Snapshot Store S8 restart! replay! S8 ! ! Journal E1 E2 E3 E4 E5 E6 E7 E8
  • state until [E8] Snapshots ! ! Snapshot Store S8 restart! replay! S8 ! ! Journal E1 E2 E3 E4 E5 E6 E7 E8
  • state until [E8] Snapshots ! ! Snapshot Store S8 S8 ! ! Journal E1 E2 E3 E4 E5 E6 E7 E8 We could delete these!
  • trait MySummer extends Processor {! var nums: List[Int]! var total: Int! ! def receive = {! case "snap" => saveSnapshot(total)! case SaveSnapshotSuccess(metadata) => // ...! case SaveSnapshotFailure(metadata, reason) => // ...! }! }! Snapshots, save Async!
  • trait MySummer extends Processor {! var nums: List[Int]! var total: Int! ! def receive = {! case "snap" => saveSnapshot(total)! case SaveSnapshotSuccess(metadata) => // ...! case SaveSnapshotFailure(metadata, reason) => // ...! }! }! Snapshots, success final case class SnapshotMetadata(! processorId: String, sequenceNr: Long, ! timestamp: Long)
  • trait MySummer extends Processor {! var nums: List[Int]! var total: Int! ! def receive = {! case "snap" => saveSnapshot(total)! case SaveSnapshotSuccess(metadata) => // ...! case SaveSnapshotFailure(metadata, reason) => // ...! }! }! Snapshots, success
  • Snapshot Recovery class Counter extends Processor {! var total = 0! ! def receive = {! case SnapshotOffer(metadata, snap: Int) => ! total = snap! ! case Persistent(payload, sequenceNr) => // ...! }! }
  • Snapshots Upsides • Simple! • Faster recovery (!) • Allows to delete “older” events • “known state at point in time”
  • Snapshots Downsides • More logic to write • Maybe not needed if events replay “fast enough” • Possibly “yet another database” to pick • snapshots are different than events, may be big!
  • Views
  • Journal (DB) ! ! ! Views ! Processor ! def processorId = “a” ! ! View ! def processorId = “a” ! ! ! pooling
  • Journal (DB) ! ! ! Views ! Processor ! def processorId = “a” ! ! View ! def processorId = “a” ! ! ! pooling ! View ! def processorId = “a” ! ! ! pooling different ActorPath, same processorId
  • View class DoublingCounterProcessor extends View {! var state = 0! 
 override val processorId = "counter"! ! def receive = {! case Persistent(payload, seqNr) =>! // “state += 2 * payload” ! ! }! }
  • Akka Persistence Plugins Plugins are Community maintained!
 (“not sure how production ready”) http://akka.io/community/#journal_plugins ! • Journals - Cassandra, HBase, Mongo … • SnapshotStores - Cassandra, HDFS, HBase, Mongo …
  • SnapshotStore Plugins! http://akka.io/community/#journal_plugins
  • Extra: Channels
  • Extras: Channel Features: • de-duplication
  • Extras: PersistentChannel Features: • “at least once delivery”
  • Try it now
  • Try it now() // ! typesafe.com/activator akka-sample-persistence-scala
  • Links • Official docs: http://doc.akka.io/docs/akka/2.3.0/scala/persistence.html • Patrik’s Slides & Webinar: http://www.slideshare.net/patriknw/akka- persistence-webinar • Papers: • Sagas http://www.cs.cornell.edu/andru/cs711/2002fa/reading/ sagas.pdf • Life beyond Distributed Transactions: http://www-db.cs.wisc.edu/ cidr/cidr2007/papers/cidr07p15.pdf • Pics: • http://misaspuppy.deviantart.com/art/Messenger-s-Cutie-Mark-A- Flying-Envelope-291040459
  • Mailing List groups.google.com/forum/#!forum/akka-user
  • Links Eric Evans: "The DDD book” Talk:“Acknowledging CAP at the Root” ! ! ! ! ! ! VaughnVernon’s Book and Talk ! !
  • ktoso @ typesafe.com twitter: ktosopl github: ktoso blog: project13.pl team blog: letitcrash.com GeeCON 2014 @ Kraków, PL ! Dzięki! Thanks! ありがとう! ! ! ping me:
  • ©Typesafe 2014 – All Rights Reserved