Escolar Documentos
Profissional Documentos
Cultura Documentos
typesafe.com
geecon.org
Java.pl / KrakowScala.pl
sckrk.com / meetup.com/Paper-Cup @ London
GDGKrakow.pl
meetup.com/Lambda-Lounge-Krakow
typesafe.com
geecon.org
Java.pl / KrakowScala.pl
sckrk.com / meetup.com/Paper-Cup @ London
GDGKrakow.pl
meetup.com/Lambda-Lounge-Krakow
typesafe.com
geecon.org
Java.pl / KrakowScala.pl
sckrk.com / meetup.com/Paper-Cup @ London
GDGKrakow.pl
meetup.com/Lambda-Lounge-Krakow
typesafe.com
geecon.org
Java.pl / KrakowScala.pl
sckrk.com / meetup.com/Paper-Cup @ London
GDGKrakow.pl
meetup.com/Lambda-Lounge-Krakow
typesafe.com
geecon.org
Java.pl / KrakowScala.pl
sckrk.com / meetup.com/Paper-Cup @ London
GDGKrakow.pl
meetup.com/Lambda-Lounge-Krakow
hAkker @
typesafe.com
geecon.org
Java.pl / KrakowScala.pl
sckrk.com / meetup.com/Paper-Cup @ London
GDGKrakow.pl
meetup.com/Lambda-Lounge-Krakow
akka-persistence
mainly by
Martin Krasser
!https://github.com/krasserm
!
inspired by:
https://github.com/eligosource/eventsourced
dependencies
Show of hands!
Show of hands!
Show of hands!
Show of hands!
sourcing styles
Command Sourcing
msg: DoThing
msg persisted before receive
imperative, do the thing
business logic change,
can be reflected in reaction
Processor
Event Sourcing
sourcing styles
Command Sourcing
Event Sourcing
msg: DoThing
msg: ThingDone
Processor
EventsourcedProcessor
Plain Actors
Actor
count: 0
!
!
Actor
An Actor that keeps count of messages it processed
count: 0
!
!
Actor
An Actor that keeps count of messages it processed
count: 0
!
!
Actor
An Actor that keeps count of messages it processed
Lets send 2 messages to it
count: 0
!
!
Actor
An Actor that keeps count of messages it processed
count: 0
!
!
Actor
!
!
class Counter extends Actor {!
var count = 0!
def receive = {!
case _ => count += 1!
}!
}
Actor
count: 0
!
!
Actor
count: 0
!
!
Actor
count: 1
!
!
Actor
count: 1
!
!
crash!
Actor
crash!
Actor
restart
Actor
restart
count: 0
!
!
Actor
restarted
count: 0
!
!
Actor
restarted
count: 1
!
!
Actor
restarted
count: 1
!
!
Actor
restarted
count: 1
!
!
wrong!
expected count == 2!
Processor
Processor
Journal
(DB)
!
!
!
var count = 0
!
def processorId = a
!
Processor
Journal
(DB)
!
!
!
var count = 0
!
def processorId = a
!
Processor
Journal
(DB)
!
!
!
var count = 0
!
def processorId = a
!
Processor
Journal
(DB)
!
!
!
var count = 0
!
def processorId = a
!
Processor
Journal
(DB)
!
!
!
var count = 1
!
def processorId = a
!
Processor
Journal
(DB)
!
!
!
var count = 1
!
def processorId = a
!
Processor
Journal
(DB)
!
!
!
var count = 1
!
def processorId = a
!
crash!
Processor
Journal
(DB)
!
!
!
Processor
Journal
(DB)
!
!
!
restart
Processor
Journal
(DB)
!
!
!
restart
var count = 0
!
def processorId = a
!
Processor
Journal
(DB)
!
restart
var count = 0
!
def processorId = a
!
!
!
replay!
Processor
Journal
(DB)
!
!
!
var count = 0
!
def processorId = a
!
Processor
Journal
(DB)
!
var count = 0
!
def processorId = a
!
!
!
replay!
Processor
Journal
(DB)
!
!
!
var count = 1
!
def processorId = a
!
Processor
Journal
(DB)
!
var count = 1
!
def processorId = a
!
!
!
replay!
Processor
Journal
(DB)
!
!
!
var count = 1
!
def processorId = a
!
Processor
Journal
(DB)
!
!
!
var count = 2
!
def processorId = a
!
Processor
Journal
(DB)
!
!
!
var count = 2
!
def processorId = a
!
yay!
Processor
import akka.persistence._!
!
class CounterProcessor extends Processor {!
var count = 0!
!
def receive = {!
case Persistent(payload, seqNr) =>!
// payload already persisted!
count += 1!
}!
}
Processor
import akka.persistence._!
!
class CounterProcessor extends Processor {!
var count = 0!
!
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!
!
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!
!
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!
!
def receive = {!
case Persistent(payload, seqNr) =>!
// payload already persisted!
count += 1!
}!
} persisted!
is already
counter ! Persistent(payload)
Processor
import akka.persistence._!
!
class CounterProcessor extends Processor {!
var count = 0!
!
def receive = {!
case Persistent(payload, seqNr) =>!
// payload already persisted!
count += 1!
sequenceNr
}!
(generated by akka)
} persisted!
is already
counter ! Persistent(payload)
Processor
import akka.persistence._!
!
class CounterProcessor extends Processor {!
var count = 0!
!
def receive = {!
case notPersisted =>!
// will not replay this msg!!
count += 1!
}!
}
counter ! payload
Processor
import akka.persistence._!
!
class CounterProcessor extends Processor {!
var count = 0!
!
def receive = {!
case notPersisted =>!
// will not replay this msg!!
count += 1!
}!
}
wont persist
counter ! payload
Processor
import akka.persistence._!
!
class CounterProcessor extends Processor {!
var count = 0!
!
def receive = {!
case notPersisted =>!
// will not replay this msg!!
count += 1!
}!
}
wont persist
counter ! payload
wont replay
Processor
import akka.persistence._!
!
class CounterProcessor extends Processor {!
var count = 0!
!
def receive = {!
case Persistent(payload, seqNr) =>!
// payload already persisted!
count += 1!
!
case notPersistentMsg =>!
// msg not persisted - like in normal Actor!
count += 1!
}!
}
Processor
Upsides
Processor
Downsides
EventsourcedProcessor
EventsourcedProcessor
(longer name == more flexibility)
;-)
EventsourcedProcessor
var count = 0
C1
!
def processorId = a
!
!
!
Journal
EventsourcedProcessor
var count = 0
C1
!
def processorId = a
!
Command
!
!
Journal
EventsourcedProcessor
var count = 0
C1
!
def processorId = a
!
!
!
Journal
EventsourcedProcessor
var count = 0
C1
!
def processorId = a
!
Generate
Events
!
!
Journal
EventsourcedProcessor
var count = 0
C1
!
def processorId = a
!
E1
!
!
Journal
EventsourcedProcessor
var count = 0
C1
!
def processorId = a
!
E1
Event
!
!
Journal
EventsourcedProcessor
var count = 0
C1
!
def processorId = a
!
E1
!
!
Journal
EventsourcedProcessor
var count = 0
C1
!
def processorId = a
!
ACK
persisted
E1
!
!
Journal
EventsourcedProcessor
var count = 1
C1
!
def processorId = a
!
E1
E1
!
!
Journal
EventsourcedProcessor
var count = 1
C1
!
def processorId = a
!
E1
Apply
event
E1
!
!
Journal
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!
!
}!
EventsourcedProcessor
def receiveCommand = {!
case ManyCommand(many) =>!
for (event <- many map AddOneEvent)!
persist(event) { state = updateState(_) }!
!
case command: Command =>!
persist(AddOneEvent(command)) { state = updateState(_) }!
}
EventsourcedProcessor
def receiveCommand = {!
case ManyCommand(many) =>!
for (event <- many map AddOneEvent)!
persist(event) { state = updateState(_) }!
!
case command: Command =>!
persist(AddOneEvent(command)) { state = updateState(_) }!
}
EventsourcedProcessor
def receiveCommand = {!
case ManyCommand(many) =>!
for (event <- many map AddOneEvent)!
persist(event) { state = updateState(_) }!
!
case command: Command =>!
persist(AddOneEvent(command)) { state = updateState(_) }!
}
EventsourcedProcessor
def receiveCommand = {!
case ManyCommand(many) =>!
for (event <- many map AddOneEvent)!
persist(event) { state = updateState(_) }!
!
case command: Command =>!
async
persist
persist(AddOneEvent(command)) { state = updateState(_) }!
} that event
EventsourcedProcessor
def receiveCommand = {!
case ManyCommand(many) =>!
for (event <- many map AddOneEvent)!
persist(event) { state = updateState(_) }!
!
case command: Command =>!
async
persist
persist(AddOneEvent(command))
{ state
async called
after = updateState(_) }!
} that event
successful persist
EventsourcedProcessor
def receiveCommand = {!
case ManyCommand(many) =>!
for (event <- many map AddOneEvent)!
persist(event) { state = updateState(_) }!
!
case command: Command =>!
async
persist
persist(AddOneEvent(command))
{ state
async called
after = updateState(_) }!
} that event
successful persist
EventsourcedProcessor
def receiveCommand = {!
case ManyCommand(many) =>!
for (event <- many map AddOneEvent)!
persist(event) { state = updateState(_) }!
!
case command: Command =>!
persist(AddOneEvent(command)) { state = updateState(_) }!
}
EventsourcedProcessor
case ManyCommand(many) =>!
for (event <- many map AddOneEvent)!
persist(event) { state = updateState(_) }
EventsourcedProcessor
case ManyCommand(many) =>!
for (event <- many map AddOneEvent)!
persist(event) { state = updateState(_) }
style fix
EventsourcedProcessor
case ManyCommand(many) =>!
for (event <- many map AddOneEvent)!
persist(event) { state = updateState(_) }
style fix
Eventsourced, recovery
Eventsourced, snapshots
Eventsourced, snapshots
/** MUST NOT SIDE-EFFECT! */!
def receiveRecover = {!
case SnapshotOffer(meta, snapshot: State) => !
this.state = state!
!
case replayedEvent: Event => !
updateState(replayedEvent)!
}
Eventsourced, snapshots
/** MUST NOT SIDE-EFFECT! */!
def receiveRecover = {!
case SnapshotOffer(meta, snapshot: State) => !
this.state = state!
!
case replayedEvent: Event => !
updateState(replayedEvent)!
}
Eventsourced, snapshots
/** MUST NOT SIDE-EFFECT! */!
def receiveRecover = {!
case SnapshotOffer(meta, snapshot: State) => !
this.state = state!
!
case replayedEvent: Event => !
snapshot!?
updateState(replayedEvent)!
}
how?
Eventsourced, snapshots
/** MUST NOT SIDE-EFFECT! */!
def receiveRecover = {!
case SnapshotOffer(meta, snapshot: State) => !
this.state = state!
!
case replayedEvent: Event => !
snapshot!?
updateState(replayedEvent)!
}
how?
def receiveCommand = {!
case command: Command =>!
saveSnapshot(state) // async!!
}
Snapshots
Snapshots
(in SnapshotStore)
Snapshots
sum of states
E1
E2
E3
E4
E5
E6
! E7
Journal
E8
Snapshots
E1
E2
E3
E4
E5
E6
! E7
Journal
E8
S8
Snapshots
E1
E2
E3
E4
E5
E6
! E7
E8
Journal
S8
snapshot!
!
!
Snapshot Store
Snapshots
E1
E2
E3
E4
E5
E6
! E7
E8
Journal
S8
!
!
Snapshot Store
S8
Snapshots
E1
E2
E3
E4
E5
E6
! E7
E8
Journal
S8
S8
crash!
!
!
Snapshot Store
Snapshots
E1
E2
E3
E4
E5
E6
! E7
E8
Journal
S8
crash!
!
!
Snapshot Store
Snapshots
restart!
E1
E2
E3
E4
E5
E6
! E7
E8
Journal
S8
!
!
Snapshot Store
bring me up-to-date!
Snapshots
restart!
replay!
E1
E2
E3
E4
E5
E6
! E7
E8
Journal
S8
!
!
Snapshot Store
bring me up-to-date!
Snapshots
restart!
replay!
E1
E2
E3
E4
E5
E6
! E7
E8
Journal
S8
!
!
Snapshot Store
bring me up-to-date!
Snapshots
restart!
replay!
E1
E2
E3
E4
E5
E6
! E7
E8
Journal
S8
!
!
Snapshot Store
bring me up-to-date!
Snapshots
restart!
replay!
E1
E2
E3
E4
bring me up-to-date!
E5
E6
! E7
E8
Journal
S8
!
!
Snapshot Store
S8
Snapshots
restart!
replay!
E1
E2
E3
E4
E5
E6
! E7
E8
Journal
S8
!
!
Snapshot Store
S8
Snapshots
E1
E2
E3
E4
E5
E6
! E7
E8
Journal
S8
!
!
Snapshot Store
S8
Snapshots
We could delete these!
E1
E2
E3
E4
E5
E6
! E7
E8
Journal
S8
!
!
Snapshot Store
S8
Snapshots, save
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
trait MySummer extends Processor {!
var nums: List[Int]!
Async!
var total: Int!
!
def receive = {!
case "snap"
=> saveSnapshot(total)!
case SaveSnapshotSuccess(metadata)
=> // ...!
case SaveSnapshotFailure(metadata, reason) => // ...!
}!
}!
Snapshots, success
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
trait MySummer extends Processor {!
var nums: List[Int]!
var total: Int!
!
def receive = {!
case "snap"
=> saveSnapshot(total)!
case SaveSnapshotSuccess(metadata)
=> // ...!
case SaveSnapshotFailure(metadata, reason) => // ...!
}!
}!
final case class SnapshotMetadata(!
processorId: String, sequenceNr: Long, !
timestamp: Long)
Snapshots, success
trait MySummer extends Processor {!
var nums: List[Int]!
var total: Int!
!
def receive = {!
case "snap"
=> saveSnapshot(total)!
case SaveSnapshotSuccess(metadata)
=> // ...!
case SaveSnapshotFailure(metadata, reason) => // ...!
}!
}!
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
Views
Views
Journal
(DB)
!
!
!
Processor
!
def processorId = a
Views
Journal
(DB)
!
Processor
!
def processorId = a
!
!
View
!
def processorId = a
!
!
!
Views
Journal
(DB)
Processor
!
def processorId = a
!
!
pooling
View
!
def processorId = a
!
!
!
Views
Journal
(DB)
Processor
!
def processorId = a
!
!
!
pooling
!
pooling
View
!
def processorId = a
!
!
!
View
!
def processorId = a
!
!
!
Views
Journal
(DB)
Processor
!
def processorId = a
!
!
!
different ActorPath,
same processorId
!
pooling
!
pooling
View
!
def processorId = a
!
!
!
View
!
def processorId = a
!
!
!
View
class DoublingCounterProcessor extends View {!
var state = 0!
!
def receive = {!
case Persistent(payload, seqNr) =>!
// state += 2 * payload !
!
}!
}
http://akka.io/community/#journal_plugins
http://akka.io/community/#journal_plugins
http://akka.io/community/#journal_plugins
SnapshotStore
http://akka.io/community/#journal_plugins
Plugins!
Links
Official docs: http://doc.akka.io/docs/akka/2.3.0/scala/persistence.html
Patriks Slides & Webinar: http://www.slideshare.net/patriknw/akkapersistence-webinar
Papers:
Sagas http://www.cs.cornell.edu/andru/cs711/2002fa/reading/
sagas.pdf
Pics:
http://misaspuppy.deviantart.com/art/Messenger-s-Cutie-Mark-AFlying-Envelope-291040459
Mailing List
groups.google.com/forum/#!forum/akka-user
Links
Eric Evans - the DDD book
http://www.amazon.com/Domain-Driven-Design-Tackling-Complexity-Software/dp/0321125215
!
!
!
!
!
!
!
!
!
Dziki!
Thanks!
!
!
ping me:
ktoso @ typesafe.com
twitter: ktosopl
github:
ktoso
blog:
project13.pl
team blog: letitcrash.com