Akka persistence == event sourcing in 30 minutes

137
Akka persistence (message sourcing in 30 minutes) Konrad 'ktoso' Malawski Scalar 2014 @ Warsaw, PL

description

Akka 2.3 introduces akka-persistence, a wonderful way of implementing event-sourced applications. Let's give it a shot and see how DDD and Akka are a match made in heaven :-)

Transcript of Akka persistence == event sourcing in 30 minutes

Page 1: Akka persistence == event sourcing in 30 minutes

Akka persistence (message sourcing in 30 minutes)

Konrad 'ktoso' Malawski Scalar 2014 @ Warsaw, PL

Page 2: Akka persistence == event sourcing in 30 minutes

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

Page 3: Akka persistence == event sourcing in 30 minutes

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

Page 4: Akka persistence == event sourcing in 30 minutes

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

Page 5: Akka persistence == event sourcing in 30 minutes

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

Page 6: Akka persistence == event sourcing in 30 minutes

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

Page 7: Akka persistence == event sourcing in 30 minutes

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 8: Akka persistence == event sourcing in 30 minutes

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

akka-persistence

https://github.com/krasserm

https://github.com/eligosource/eventsourced

Page 9: Akka persistence == event sourcing in 30 minutes

dependencies

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

Page 10: Akka persistence == event sourcing in 30 minutes

Show of hands!

Page 11: Akka persistence == event sourcing in 30 minutes

Show of hands!

Page 12: Akka persistence == event sourcing in 30 minutes

Show of hands!

Page 13: Akka persistence == event sourcing in 30 minutes

Show of hands!

Page 14: Akka persistence == event sourcing in 30 minutes

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 15: Akka persistence == event sourcing in 30 minutes

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 16: Akka persistence == event sourcing in 30 minutes

Plain Actors

Page 17: Akka persistence == event sourcing in 30 minutes

count: 0 !

!

Actor

Page 18: Akka persistence == event sourcing in 30 minutes

count: 0 !

!

ActorAn Actor that keeps count of messages it processed

Page 19: Akka persistence == event sourcing in 30 minutes

count: 0 !

!

ActorAn Actor that keeps count of messages it processed

Page 20: Akka persistence == event sourcing in 30 minutes

count: 0 !

!

ActorAn Actor that keeps count of messages it processed

Let’s send 2 messages to it

Page 21: Akka persistence == event sourcing in 30 minutes

count: 0 !

!

ActorAn Actor that keeps count of messages it processed

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

Page 22: Akka persistence == event sourcing in 30 minutes

Actor

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

Page 23: Akka persistence == event sourcing in 30 minutes

count: 0 !

!

Actor

Page 24: Akka persistence == event sourcing in 30 minutes

count: 0 !

!

Actor

Page 25: Akka persistence == event sourcing in 30 minutes

count: 1 !

!

Actor

Page 26: Akka persistence == event sourcing in 30 minutes

count: 1 !

!

Actor

crash!

Page 27: Akka persistence == event sourcing in 30 minutes

Actor

crash!

Page 28: Akka persistence == event sourcing in 30 minutes

Actor

restart

Page 29: Akka persistence == event sourcing in 30 minutes

count: 0 !

!

Actor

restart

Page 30: Akka persistence == event sourcing in 30 minutes

count: 0 !

!

Actor

restarted

Page 31: Akka persistence == event sourcing in 30 minutes

count: 1 !

!

Actor

restarted

Page 32: Akka persistence == event sourcing in 30 minutes

count: 1 !

!

Actor

restarted

Page 33: Akka persistence == event sourcing in 30 minutes

count: 1 !

!wrong!

expected count == 2!

Actor

restarted

Page 34: Akka persistence == event sourcing in 30 minutes

Processor

Page 35: Akka persistence == event sourcing in 30 minutes

var count = 0 !

def processorId = “a” !

Journal (DB)

!

!

!

Processor

Page 36: Akka persistence == event sourcing in 30 minutes

Journal (DB)

!

!

!

Processor

var count = 0 !

def processorId = “a” !

Page 37: Akka persistence == event sourcing in 30 minutes

Journal (DB)

!

!

!

Processor

var count = 0 !

def processorId = “a” !

Page 38: Akka persistence == event sourcing in 30 minutes

Journal (DB)

!

!

!

Processor

var count = 0 !

def processorId = “a” !

Page 39: Akka persistence == event sourcing in 30 minutes

Journal (DB)

!

!

!

Processor

var count = 1 !

def processorId = “a” !

Page 40: Akka persistence == event sourcing in 30 minutes

Journal (DB)

!

!

!

Processor

var count = 1 !

def processorId = “a” !

Page 41: Akka persistence == event sourcing in 30 minutes

Journal (DB)

!

!

!

Processor

var count = 1 !

def processorId = “a” !

crash!

Page 42: Akka persistence == event sourcing in 30 minutes

Journal (DB)

!

!

!

Processor

Page 43: Akka persistence == event sourcing in 30 minutes

Journal (DB)

!

!

!

Processor

restart

Page 44: Akka persistence == event sourcing in 30 minutes

Journal (DB)

!

!

!

Processor

var count = 0 !

def processorId = “a” !

restart

Page 45: Akka persistence == event sourcing in 30 minutes

Journal (DB)

!

!

!

Processor

var count = 0 !

def processorId = “a” !

replay!

restart

Page 46: Akka persistence == event sourcing in 30 minutes

Journal (DB)

!

!

!

Processor

var count = 0 !

def processorId = “a” !

Page 47: Akka persistence == event sourcing in 30 minutes

Journal (DB)

!

!

!

Processor

var count = 0 !

def processorId = “a” !

replay!

Page 48: Akka persistence == event sourcing in 30 minutes

Journal (DB)

!

!

!

Processor

var count = 1 !

def processorId = “a” !

Page 49: Akka persistence == event sourcing in 30 minutes

Journal (DB)

!

!

!

Processor

var count = 1 !

def processorId = “a” !

replay!

Page 50: Akka persistence == event sourcing in 30 minutes

Journal (DB)

!

!

!

Processor

var count = 1 !

def processorId = “a” !

Page 51: Akka persistence == event sourcing in 30 minutes

Journal (DB)

!

!

!

Processor

var count = 2 !

def processorId = “a” !

Page 52: Akka persistence == event sourcing in 30 minutes

Journal (DB)

!

!

!

Processor

var count = 2 !

def processorId = “a” !

yay!

Page 53: Akka persistence == event sourcing in 30 minutes

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

Page 54: Akka persistence == event sourcing in 30 minutes

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

counter ! Persistent(payload)

Page 55: Akka persistence == event sourcing in 30 minutes

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

counter ! Persistent(payload)

Page 56: Akka persistence == event sourcing in 30 minutes

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

counter ! Persistent(payload)

Page 57: Akka persistence == event sourcing in 30 minutes

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

counter ! Persistent(payload)

is already persisted!

Page 58: Akka persistence == event sourcing in 30 minutes

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

counter ! Persistent(payload)

sequenceNr (generated by akka)

is already persisted!

Page 59: Akka persistence == event sourcing in 30 minutes

Processorimport akka.persistence._!!class CounterProcessor extends Processor {! var count = 0! override val processorId = "counter"!! def receive = {! case notPersisted =>! // will not replay this msg!! count += 1! }!}

counter ! payload

Page 60: Akka persistence == event sourcing in 30 minutes

Processorimport akka.persistence._!!class CounterProcessor extends Processor {! var count = 0! override val processorId = "counter"!! def receive = {! case notPersisted =>! // will not replay this msg!! count += 1! }!}

counter ! payload

won’t persist

Page 61: Akka persistence == event sourcing in 30 minutes

Processorimport akka.persistence._!!class CounterProcessor extends Processor {! var count = 0! override val processorId = "counter"!! def receive = {! case notPersisted =>! // will not replay this msg!! count += 1! }!}

counter ! payload

won’t persist

won’t replay

Page 62: Akka persistence == event sourcing in 30 minutes

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 63: Akka persistence == event sourcing in 30 minutes

ProcessorUpsides

• 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 64: Akka persistence == event sourcing in 30 minutes

ProcessorDownsides

• 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 65: Akka persistence == event sourcing in 30 minutes

EventsourcedProcessor

Page 66: Akka persistence == event sourcing in 30 minutes

EventsourcedProcessor (longer name == more flexibility);-)

Page 67: Akka persistence == event sourcing in 30 minutes

super quick domain modeling!

Page 68: Akka persistence == event sourcing in 30 minutes

super quick domain modeling!

sealed trait Command!case class ManyCommand(nums: List[Int]) extends Command

Commands - input from user, “send emails”, not persisted

Page 69: Akka persistence == event sourcing in 30 minutes

super quick domain modeling!

sealed trait Command!case class ManyCommand(nums: List[Int]) extends Command

Commands - input from user, “send emails”, not persisted

sealed trait Event!case class AddOneEvent(num: Int) extends Event!

Events - business events emitted by the processor, persisted

Page 70: Akka persistence == event sourcing in 30 minutes

super quick domain modeling!

sealed trait Command!case class ManyCommand(nums: List[Int]) extends Command

Commands - input from user, “send emails”, not persisted

case class State(count: Int) {! def updated(more: Int) = State(count + more)!}

State - internal actor state, kept in var

sealed trait Event!case class AddOneEvent(num: Int) extends Event!

Events - business events emitted by the processor, persisted

Page 71: Akka persistence == event sourcing in 30 minutes

var count = 0 !

def processorId = “a” !

EventsourcedProcessor

!

!

Journal

C1

Page 72: Akka persistence == event sourcing in 30 minutes

var count = 0 !

def processorId = “a” !

EventsourcedProcessor

Command

!

!

Journal

C1

Page 73: Akka persistence == event sourcing in 30 minutes

var count = 0 !

def processorId = “a” !

EventsourcedProcessor

!

!

Journal

C1

Page 74: Akka persistence == event sourcing in 30 minutes

var count = 0 !

def processorId = “a” !

EventsourcedProcessor

!

!

Journal

C1

Generate Events

Page 75: Akka persistence == event sourcing in 30 minutes

C1

var count = 0 !

def processorId = “a” !

EventsourcedProcessor

!

!

Journal

E1

Page 76: Akka persistence == event sourcing in 30 minutes

C1

var count = 0 !

def processorId = “a” !

EventsourcedProcessor

!

!

Journal

E1

Event

Page 77: Akka persistence == event sourcing in 30 minutes

C1

var count = 0 !

def processorId = “a” !

EventsourcedProcessor

!

!

Journal

E1

Page 78: Akka persistence == event sourcing in 30 minutes

C1

var count = 0 !

def processorId = “a” !

EventsourcedProcessor

!

!

Journal

E1

ACK “persisted”

Page 79: Akka persistence == event sourcing in 30 minutes

C1

var count = 1 !

def processorId = “a” !

EventsourcedProcessor

!

!

Journal

E1

E1

Page 80: Akka persistence == event sourcing in 30 minutes

C1

var count = 1 !

def processorId = “a” !

EventsourcedProcessor

!

!

Journal

E1

E1

“Apply” event

Page 81: Akka persistence == event sourcing in 30 minutes

EventsourcedProcessor

class MultiCounter extends EventsourcedProcessor {!! var state = State(count = 0)!! def updateState(e: Event): State = {! case event: AddOneEvent => state.updated(event.num)! }! ! // API:!! def receiveCommand = ??? // TODO!! def receiveRecover = ??? // TODO!!}!

Page 82: Akka persistence == event sourcing in 30 minutes

EventsourcedProcessor

def receiveCommand = {! case ManyCommand(many) =>! for (event <- many map AddOneEvent)! persist(event) { state = updateState(_) }!! case command: Command =>! persist(AddOneEvent(command)) { state = updateState(_) }!}

Page 83: Akka persistence == event sourcing in 30 minutes

EventsourcedProcessor

def receiveCommand = {! case ManyCommand(many) =>! for (event <- many map AddOneEvent)! persist(event) { state = updateState(_) }!! case command: Command =>! persist(AddOneEvent(command)) { state = updateState(_) }!}

Page 84: Akka persistence == event sourcing in 30 minutes

EventsourcedProcessor

def receiveCommand = {! case ManyCommand(many) =>! for (event <- many map AddOneEvent)! persist(event) { state = updateState(_) }!! case command: Command =>! persist(AddOneEvent(command)) { state = updateState(_) }!}

Page 85: Akka persistence == event sourcing in 30 minutes

EventsourcedProcessor

def receiveCommand = {! case ManyCommand(many) =>! for (event <- many map AddOneEvent)! persist(event) { state = updateState(_) }!! case command: Command =>! persist(AddOneEvent(command)) { state = updateState(_) }!}

async persist that event

Page 86: Akka persistence == event sourcing in 30 minutes

EventsourcedProcessor

def receiveCommand = {! case ManyCommand(many) =>! for (event <- many map AddOneEvent)! persist(event) { state = updateState(_) }!! case command: Command =>! persist(AddOneEvent(command)) { state = updateState(_) }!}

async persist that event

async called after successful persist

Page 87: Akka persistence == event sourcing in 30 minutes

EventsourcedProcessor

def receiveCommand = {! case ManyCommand(many) =>! for (event <- many map AddOneEvent)! persist(event) { state = updateState(_) }!! case command: Command =>! persist(AddOneEvent(command)) { state = updateState(_) }!}

async persist that event

async called after successful persist

It is guaranteed that no new commands will be received by a processor between a call to `persist` and the execution of its `handler`.

Page 88: Akka persistence == event sourcing in 30 minutes

EventsourcedProcessor

def receiveCommand = {! case ManyCommand(many) =>! for (event <- many map AddOneEvent)! persist(event) { state = updateState(_) }!! case command: Command =>! persist(AddOneEvent(command)) { state = updateState(_) }!}

Page 89: Akka persistence == event sourcing in 30 minutes

EventsourcedProcessorcase ManyCommand(many) =>! for (event <- many map AddOneEvent)! persist(event) { state = updateState(_) }

Page 90: Akka persistence == event sourcing in 30 minutes

EventsourcedProcessorcase ManyCommand(many) =>! for (event <- many map AddOneEvent)! persist(event) { state = updateState(_) }

“style fix”

Page 91: Akka persistence == event sourcing in 30 minutes

EventsourcedProcessorcase ManyCommand(many) =>! for (event <- many map AddOneEvent)! persist(event) { state = updateState(_) }

case ManyCommand(many) =>! persist(many map AddOneEvent) { state = updateState(_) }

“style fix”

Page 92: Akka persistence == event sourcing in 30 minutes

Eventsourced, recovery

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

Page 93: Akka persistence == event sourcing in 30 minutes

Eventsourced, snapshots

Page 94: Akka persistence == event sourcing in 30 minutes

Eventsourced, snapshots/** MUST NOT SIDE-EFFECT! */!def receiveRecover = {! case SnapshotOffer(meta, snapshot: State) => ! this.state = state!! case replayedEvent: Event => ! updateState(replayedEvent)!}

Page 95: Akka persistence == event sourcing in 30 minutes

Eventsourced, snapshots/** MUST NOT SIDE-EFFECT! */!def receiveRecover = {! case SnapshotOffer(meta, snapshot: State) => ! this.state = state!! case replayedEvent: Event => ! updateState(replayedEvent)!}

Page 96: Akka persistence == event sourcing in 30 minutes

Eventsourced, snapshots/** MUST NOT SIDE-EFFECT! */!def receiveRecover = {! case SnapshotOffer(meta, snapshot: State) => ! this.state = state!! case replayedEvent: Event => ! updateState(replayedEvent)!}

snapshot!? how?

Page 97: Akka persistence == event sourcing in 30 minutes

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 98: Akka persistence == event sourcing in 30 minutes

Snapshots

Page 99: Akka persistence == event sourcing in 30 minutes

Snapshots (in SnapshotStore)

Page 100: Akka persistence == event sourcing in 30 minutes

…sum of states…

Snapshots

!

!

Journal

E1 E2 E3 E4

E5 E6 E7 E8

Page 101: Akka persistence == event sourcing in 30 minutes

state until [E8]

Snapshots

S8!

!

Journal

E1 E2 E3 E4

E5 E6 E7 E8

Page 102: Akka persistence == event sourcing in 30 minutes

state until [E8]

Snapshots

S8

!

!

Snapshot Store

snapshot!

!

!

Journal

E1 E2 E3 E4

E5 E6 E7 E8

Page 103: Akka persistence == event sourcing in 30 minutes

state until [E8]

Snapshots

S8

!

!

Snapshot Store

!

!

Journal

E1 E2 E3 E4

E5 E6 E7 E8

S8

Page 104: Akka persistence == event sourcing in 30 minutes

state until [E8]

Snapshots

S8

!

!

Snapshot Store

!

!

Journal

E1 E2 E3 E4

E5 E6 E7 E8

S8

crash!

Page 105: Akka persistence == event sourcing in 30 minutes

Snapshots

!

!

Snapshot Store

!

!

Journal

E1 E2 E3 E4

E5 E6 E7 E8

S8

crash!

Page 106: Akka persistence == event sourcing in 30 minutes

“bring me up-to-date!”

Snapshots

!

!

Snapshot Store

!

!

Journal

E1 E2 E3 E4

E5 E6 E7 E8

S8

restart!

Page 107: Akka persistence == event sourcing in 30 minutes

“bring me up-to-date!”

Snapshots

!

!

Snapshot Store

!

!

Journal

E1 E2 E3 E4

E5 E6 E7 E8

S8

restart!replay!

Page 108: Akka persistence == event sourcing in 30 minutes

“bring me up-to-date!”

Snapshots

!

!

Snapshot Store

S8

restart!replay!

!

!

Journal

E1 E2 E3 E4

E5 E6 E7 E8

Page 109: Akka persistence == event sourcing in 30 minutes

“bring me up-to-date!”

Snapshots

!

!

Snapshot Store

S8

restart!replay!

!

!

Journal

E1 E2 E3 E4

E5 E6 E7 E8

Page 110: Akka persistence == event sourcing in 30 minutes

“bring me up-to-date!”

Snapshots

!

!

Snapshot Store

S8

restart!replay!

S8!

!

Journal

E1 E2 E3 E4

E5 E6 E7 E8

Page 111: Akka persistence == event sourcing in 30 minutes

state until [E8]

Snapshots

!

!

Snapshot Store

S8

restart!replay!

S8!

!

Journal

E1 E2 E3 E4

E5 E6 E7 E8

Page 112: Akka persistence == event sourcing in 30 minutes

state until [E8]

Snapshots

!

!

Snapshot Store

S8

S8!

!

Journal

E1 E2 E3 E4

E5 E6 E7 E8

Page 113: Akka persistence == event sourcing in 30 minutes

state until [E8]

Snapshots

!

!

Snapshot Store

S8

S8!

!

Journal

E1 E2 E3 E4

E5 E6 E7 E8

We could delete these!

Page 114: Akka persistence == event sourcing in 30 minutes

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

Page 115: Akka persistence == event sourcing in 30 minutes

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 116: Akka persistence == event sourcing in 30 minutes

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 117: Akka persistence == event sourcing in 30 minutes

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 118: Akka persistence == event sourcing in 30 minutes

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 119: Akka persistence == event sourcing in 30 minutes

Snapshot Recovery

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

Page 120: Akka persistence == event sourcing in 30 minutes

SnapshotsUpsides

• Simple!

• Faster recovery (!)

• Allows to delete “older” events

• “known state at point in time”

Page 121: Akka persistence == event sourcing in 30 minutes

SnapshotsDownsides

• 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 122: Akka persistence == event sourcing in 30 minutes

Views

Page 123: Akka persistence == event sourcing in 30 minutes

Journal (DB)

!

!

!

Views!

Processor !

def processorId = “a” !

Page 124: Akka persistence == event sourcing in 30 minutes

Journal (DB)

!

!

!

Views!

Processor !

def processorId = “a” !

!View

!def processorId = “a”

!!!

Page 125: Akka persistence == event sourcing in 30 minutes

Journal (DB)

!

!

!

Views!

Processor !

def processorId = “a” !

!View

!def processorId = “a”

!!!

pooling

Page 126: Akka persistence == event sourcing in 30 minutes

Journal (DB)

!

!

!

Views!

Processor !

def processorId = “a” !

!View

!def processorId = “a”

!!!

pooling

!View

!def processorId = “a”

!!!

pooling

Page 127: Akka persistence == event sourcing in 30 minutes

Journal (DB)

!

!

!

Views!

Processor !

def processorId = “a” !

!View

!def processorId = “a”

!!!

pooling

!View

!def processorId = “a”

!!!

pooling

different ActorPath, same processorId

Page 128: Akka persistence == event sourcing in 30 minutes

View

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

Page 129: Akka persistence == event sourcing in 30 minutes

Akka Persistence Plugins

Page 130: Akka persistence == event sourcing in 30 minutes

Akka Persistence PluginsPlugins are Community maintained!

(“not sure how production ready”)

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

Page 131: Akka persistence == event sourcing in 30 minutes

Akka Persistence PluginsPlugins are Community maintained!

(“not sure how production ready”)

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

• Journals - Cassandra, HBase, Mongo …

Page 132: Akka persistence == event sourcing in 30 minutes

Akka Persistence PluginsPlugins are Community maintained!

(“not sure how production ready”)

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

• Journals - Cassandra, HBase, Mongo …

• SnapshotStores - Cassandra, HDFS, HBase, Mongo …

Page 133: Akka persistence == event sourcing in 30 minutes

SnapshotStore Plugins!

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

Page 134: Akka persistence == event sourcing in 30 minutes

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 135: Akka persistence == event sourcing in 30 minutes

Mailing List

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

Page 136: Akka persistence == event sourcing in 30 minutes

LinksEric Evans - “the DDD book”

http://www.amazon.com/Domain-Driven-Design-Tackling-Complexity-Software/dp/0321125215 !!!!!!

Vaughn Vernon’s Book and Talk http://www.amazon.com/Implementing-Domain-Driven-Design-Vaughn-Vernon/dp/0321834577

video: https://skillsmatter.com/skillscasts/4698-reactive-ddd-with-scala-and-akka !!!

Page 137: Akka persistence == event sourcing in 30 minutes

ktoso @ typesafe.com twitter: ktosopl github: ktoso blog: project13.pl team blog: letitcrash.com Scalar 2014 @ Warsaw, PL

!

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

!

ping me: