ddding tools = akka persistence

106
DDDing with Akka Persistence Konrad 'ktoso' Malawski GeeCON 2014 @ Kraków, PL Konrad `@ktosopl` Malawski

Upload: konrad-malawski

Post on 08-May-2015

3.522 views

Category:

Technology


5 download

DESCRIPTION

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.

TRANSCRIPT

Page 1: DDDing Tools = Akka Persistence

DDDing with Akka Persistence !

Konrad 'ktoso' Malawski GeeCON 2014 @ Kraków, PL

Konrad `@ktosopl` Malawski

Page 2: DDDing Tools = Akka Persistence

Konrad `@ktosopl` Malawski

hAkker @

Page 3: DDDing Tools = Akka Persistence

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 @

Page 4: DDDing Tools = Akka Persistence

mainly by Martin Krasser !!(as contractor for Typesafe) !inspired by:

https://github.com/krasserm

https://github.com/eligosource/eventsourced

akka-persistence

Page 5: DDDing Tools = Akka Persistence

Dependencies

libraryDependencies ++= Seq(! "com.typesafe.akka" %% “akka-actor" % "2.3.1",! "com.typesafe.akka" %% "akka-persistence-experimental" % "2.3.1"!)

Page 6: DDDing Tools = Akka Persistence

Show of hands!

Page 7: DDDing Tools = Akka Persistence

Show of hands!

Page 8: DDDing Tools = Akka Persistence

Show of hands!

Page 9: DDDing Tools = Akka Persistence

Show of hands!

Page 10: DDDing Tools = Akka Persistence

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

Page 11: DDDing Tools = Akka Persistence

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

Page 12: DDDing Tools = Akka Persistence

Compared to “Good ol’ CRUD Model”

state

“Mutable Record”

state =

apply(es)

“Series of Events”

Page 13: DDDing Tools = Akka Persistence

Actors

Page 14: DDDing Tools = Akka Persistence

count: 0 !

!

Actor

An Actor that keeps count of messages it processed !

Let’s send 2 messages to it (it’s “commands”)

Page 15: DDDing Tools = Akka Persistence

Actor

!!class Counter extends Actor {! var count = 0! def receive = {! case _ => count += 1! }!}

Page 16: DDDing Tools = Akka Persistence

count: 0 !

!

Actor

Page 17: DDDing Tools = Akka Persistence

count: 0 !

!

Actor

Page 18: DDDing Tools = Akka Persistence

count: 1 !

!

Actor

crash!

Page 19: DDDing Tools = Akka Persistence

Actor

crash!

Page 20: DDDing Tools = Akka Persistence

Actor

restart

Page 21: DDDing Tools = Akka Persistence

count: 0 !

!

Actor

restart

Page 22: DDDing Tools = Akka Persistence

count: 0 !

!

Actor

restarted

Page 23: DDDing Tools = Akka Persistence

count: 1 !

!

Actor

restarted

Page 24: DDDing Tools = Akka Persistence

count: 1 !

!wrong!

expected count == 2!

Actor

restarted

Page 25: DDDing Tools = Akka Persistence

Consistency Boundary

Page 26: DDDing Tools = Akka Persistence

Consistency Boundary equals Async Boundary

Page 27: DDDing Tools = Akka Persistence

Boundaries

actor

actor

async

Page 28: DDDing Tools = Akka Persistence

Boundaries

aggregate

aggregate

“eventual”

A.K.A. async

Page 29: DDDing Tools = Akka Persistence

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

consistentconsistent

eventually consistent

Page 30: DDDing Tools = Akka Persistence

Let’s open the toolbox

Page 31: DDDing Tools = Akka Persistence

Processor

Page 32: DDDing Tools = Akka Persistence

Processor (command sourcing)

Page 33: DDDing Tools = Akka Persistence

var count = 0 !

def processorId = “a” !

Journal (DB)

!

!

!

Processor

Page 34: DDDing Tools = Akka Persistence

Journal (DB)

!

!

!

Processor

var count = 0 !

def processorId = “a” !

Page 35: DDDing Tools = Akka Persistence

Journal (DB)

!

!

!

Processor

var count = 0 !

def processorId = “a” !

Page 36: DDDing Tools = Akka Persistence

Journal (DB)

!

!

!

Processor

var count = 0 !

def processorId = “a” !

Page 37: DDDing Tools = Akka Persistence

Journal (DB)

!

!

!

Processor

var count = 1 !

def processorId = “a” !

Page 38: DDDing Tools = Akka Persistence

Journal (DB)

!

!

!

Processor

var count = 1 !

def processorId = “a” !

crash!

Page 39: DDDing Tools = Akka Persistence

Journal (DB)

!

!

!

Processor

restart

Page 40: DDDing Tools = Akka Persistence

Journal (DB)

!

!

!

Processor

var count = 0 !

def processorId = “a” !

restart

Page 41: DDDing Tools = Akka Persistence

Journal (DB)

!

!

!

Processor

var count = 0 !

def processorId = “a” !

replay!

restart

Page 42: DDDing Tools = Akka Persistence

Journal (DB)

!

!

!

Processor

var count = 0 !

def processorId = “a” !

replay!

Page 43: DDDing Tools = Akka Persistence

Journal (DB)

!

!

!

Processor

var count = 1 !

def processorId = “a” !

replay!

Page 44: DDDing Tools = Akka Persistence

Journal (DB)

!

!

!

Processor

var count = 1 !

def processorId = “a” !

Page 45: DDDing Tools = Akka Persistence

Journal (DB)

!

!

!

Processor

var count = 2 !

def processorId = “a” !

yay!

Page 46: DDDing Tools = Akka Persistence

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! }!}

Page 47: DDDing Tools = Akka Persistence

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)

Page 48: DDDing Tools = Akka Persistence

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)

Page 49: DDDing Tools = Akka Persistence

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!

Page 50: DDDing Tools = Akka Persistence

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

Page 51: DDDing Tools = Akka Persistence

Processorimport 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! }!}

Page 52: DDDing Tools = Akka Persistence

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!)

Page 53: DDDing Tools = Akka Persistence

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)

Page 54: DDDing Tools = Akka Persistence

Eventsourced Processor

Page 55: DDDing Tools = Akka Persistence

Eventsourced Processor (event sourcing)

Page 56: DDDing Tools = Akka Persistence

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

Page 57: DDDing Tools = Akka Persistence

var state = S0 !

def processorId = “a” !

EventsourcedProcessor

Command

!

!

Journal

Page 58: DDDing Tools = Akka Persistence

EventsourcedProcessor

var state = S0 !

def processorId = “a” !

!

!

Journal

Generate Events

Page 59: DDDing Tools = Akka Persistence

EventsourcedProcessor

var state = S0 !

def processorId = “a” !

!

!

Journal

Generate Events

E1

Page 60: DDDing Tools = Akka Persistence

EventsourcedProcessor

ACK “persisted”

!

!

Journal

E1

var state = S0 !

def processorId = “a” !

Page 61: DDDing Tools = Akka Persistence

EventsourcedProcessor

“Apply” event

!

!

Journal

E1

var state = S0 !

def processorId = “a” !

E1

Page 62: DDDing Tools = Akka Persistence

EventsourcedProcessor

!

!

Journal

E1

var state = S0 !

def processorId = “a” !

E1

Okey!

Page 63: DDDing Tools = Akka Persistence

EventsourcedProcessor

!

!

Journal

E1

var state = S0 !

def processorId = “a” !

E1

Okey!

Page 64: DDDing Tools = Akka Persistence

EventsourcedProcessor

!

!

Journal

E1

var state = S0 !

def processorId = “a” !

E1

Ok, he got my $.

Page 65: DDDing Tools = Akka Persistence

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!!}!

Page 66: DDDing Tools = Akka Persistence

EventsourcedProcessor

def receiveCommand = {!! case TakeMy(geeCoins) =>! persist(BalanceChangedBy(geeCoins)) { changed =>! state = updateState(changed) ! }!!!!!!!}

async callback

Page 67: DDDing Tools = Akka Persistence

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

Page 68: DDDing Tools = Akka Persistence

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?!

Page 69: DDDing Tools = Akka Persistence

Ordering guarantees

!

!

Journal

E1

var state = S0 !

def processorId = “a” !

C1C2

C3

Page 70: DDDing Tools = Akka Persistence

Ordering guarantees

!

!

Journal

E1

var state = S0 !

def processorId = “a” !

C1C2

C3

Commands get “stashed” until processing C1’s events are acted upon.

Page 71: DDDing Tools = Akka Persistence

!

!

Journal

Ordering guarantees

var state = S0 !

def processorId = “a” !

C1C2

C3 E1

E2

E2E1

events get applied in-order

Page 72: DDDing Tools = Akka Persistence

C2

!

!

Journal

Ordering guarantees

var state = S0 !

def processorId = “a” !

C3 E1 E2

E2E1

and the cycle repeats

Page 73: DDDing Tools = Akka Persistence

Eventsourced, recovery

/** MUST NOT SIDE-EFFECT! */!def receiveRecover = {! case replayedEvent: Event => ! updateState(replayedEvent)!}

exact same code for all events!

Page 74: DDDing Tools = Akka Persistence

Snapshots

Page 75: DDDing Tools = Akka Persistence

Snapshots (in SnapshotStore)

Page 76: DDDing Tools = Akka Persistence

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?

Page 77: DDDing Tools = Akka Persistence

…sum of states…

Snapshots

!

!

Journal

E1 E2 E3 E4

E5 E6 E7 E8

Page 78: DDDing Tools = Akka Persistence

state until [E8]

Snapshots

S8

!

!

Snapshot Store

snapshot!

!

!

Journal

E1 E2 E3 E4

E5 E6 E7 E8

Page 79: DDDing Tools = Akka Persistence

state until [E8]

Snapshots

S8

!

!

Snapshot Store

!

!

Journal

E1 E2 E3 E4

E5 E6 E7 E8

S8

crash!

Page 80: DDDing Tools = Akka Persistence

Snapshots

!

!

Snapshot Store

!

!

Journal

E1 E2 E3 E4

E5 E6 E7 E8

S8

crash!

Page 81: DDDing Tools = Akka Persistence

“bring me up-to-date!”

Snapshots

!

!

Snapshot Store

!

!

Journal

E1 E2 E3 E4

E5 E6 E7 E8

S8

restart!replay!

Page 82: DDDing Tools = Akka Persistence

“bring me up-to-date!”

Snapshots

!

!

Snapshot Store

S8

restart!replay!

S8!

!

Journal

E1 E2 E3 E4

E5 E6 E7 E8

Page 83: DDDing Tools = Akka Persistence

state until [E8]

Snapshots

!

!

Snapshot Store

S8

restart!replay!

S8!

!

Journal

E1 E2 E3 E4

E5 E6 E7 E8

Page 84: DDDing Tools = Akka Persistence

state until [E8]

Snapshots

!

!

Snapshot Store

S8

S8!

!

Journal

E1 E2 E3 E4

E5 E6 E7 E8

We could delete these!

Page 85: DDDing Tools = Akka Persistence

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!

Page 86: DDDing Tools = Akka Persistence

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)

Page 87: DDDing Tools = Akka Persistence

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

Page 88: DDDing Tools = Akka Persistence

Snapshot Recovery

class Counter extends Processor {! var total = 0! ! def receive = {! case SnapshotOffer(metadata, snap: Int) => ! total = snap!! case Persistent(payload, sequenceNr) => // ...! }!}

Page 89: DDDing Tools = Akka Persistence

Snapshots

Upsides

• Simple!

• Faster recovery (!)

• Allows to delete “older” events

• “known state at point in time”

Page 90: DDDing Tools = Akka Persistence

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!

Page 91: DDDing Tools = Akka Persistence

Views

Page 92: DDDing Tools = Akka Persistence

Journal (DB)

!

!

!

Views

!Processor

!def processorId = “a”

!

!View

!def processorId = “a”

!!!

pooling

Page 93: DDDing Tools = Akka Persistence

Journal (DB)

!

!

!

Views

!Processor

!def processorId = “a”

!

!View

!def processorId = “a”

!!!

pooling

!View

!def processorId = “a”

!!!

pooling

different ActorPath, same processorId

Page 94: DDDing Tools = Akka Persistence

View

class DoublingCounterProcessor extends View {! var state = 0! override val processorId = "counter"!! def receive = {! case Persistent(payload, seqNr) =>! // “state += 2 * payload” !! }!}

Page 95: DDDing Tools = Akka Persistence

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 …

Page 96: DDDing Tools = Akka Persistence

SnapshotStore Plugins!

http://akka.io/community/#journal_plugins

Page 97: DDDing Tools = Akka Persistence

Extra: Channels

Page 98: DDDing Tools = Akka Persistence

Extras: Channel

Features:

• de-duplication

Page 99: DDDing Tools = Akka Persistence

Extras: PersistentChannel

Features:

• “at least once delivery”

Page 100: DDDing Tools = Akka Persistence

Try it now

Page 101: DDDing Tools = Akka Persistence

Try it now() // !

typesafe.com/activator

akka-sample-persistence-scala

Page 102: DDDing Tools = Akka Persistence

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

Page 103: DDDing Tools = Akka Persistence

Mailing List

groups.google.com/forum/#!forum/akka-user

Page 105: DDDing Tools = Akka Persistence

ktoso @ typesafe.com twitter: ktosopl github: ktoso blog: project13.pl team blog: letitcrash.com GeeCON 2014 @ Kraków, PL

!

Dzięki! Thanks! ありがとう! !

!

ping me:

Page 106: DDDing Tools = Akka Persistence

©Typesafe 2014 – All Rights Reserved