This document discusses Akka persistence and command/event sourcing. It introduces key concepts like command and event sourcing where state is represented as a log of events, and commands generate events. It describes how Akka persistence allows storing commands/events to a journal and replaying them on recovery. It also discusses views which allow querying the event log, and using Akka persistence in a clustered environment with techniques like cluster singleton and cluster sharding.
3. Resilient
• Embrace Failure
• Failure is a normal part of the application lifecycle
• Self Heal
• Failure is detected, isolated, and managed
Akka
Persistence
ScalaDays
2014
4. The Naïve Way
• Write State to Database
• Transactions Everywhere
• Problem Solved?
• Not Scalable, Responsive, Event-Driven!
Akka
Persistence
ScalaDays
2014
6. Command and Event Sourcing
• State is the sum of Events
• Events are persisted to Store
• Append only
• Scales well
Akka
Persistence
ScalaDays
2014
7. Command v.s. Event
• Command
• What someone wants me to do
• Can be rejected
• Event
• Something that has already happened
• An immutable fact
Akka
Persistence
ScalaDays
2014
8. Commands can Generate Events
• If I accept a Command and change State
• Persist Event to Store
• If I crash
• Replay Events to recover State
Akka
Persistence
ScalaDays
2014
9. Persist All Commands?
• If I crash on a Command
• I will likely crash during recovery
• Like the Army
• Don't question orders
• Repeat until success
Akka
Persistence
ScalaDays
2014
10. Only Persist Events
• Only accepted Commands generate Events
• No surprises during recovery
• Like a dieting method
• You are what you eat
Akka
Persistence
ScalaDays
2014
11. Achievement Unlocked?
• Resilient
• State is recoverable
• Scalable
• Append only writes
• Something Missing?
• Queries
Akka
Persistence
ScalaDays
2014
13. CQRS
• Separate Models
• Command Model
• Optimized for command processing
• Query Model
• Optimized data presentation
Akka
Persistence
ScalaDays
2014
14. Query Model from Events
• Source the Events
• Pick what fits
• In Memory
• SQL Database
• Graph Database
• Key Value Store
Akka
Persistence
ScalaDays
2014
18. super quick domain modelling!
sealed trait Command!
case class GiveMe(coins: Int) extends Command!
case class TakeMy(coins: Int) extends Command
Commands - what others “tell” us; not persisted
case class Wallet(coins: Int) {!
def updated(diff: Int) = State(coins + diff)!
}
State - reflection of a series of events
sealed trait Event!
case class BalanceChangedBy(coins: Int) extends Event!
Events - reflect effects, past tense; persisted
19. var state = S0
!
def processorId = “a”
!
PersistentActor
Command
!
!
Journal
48. Eventsourced, recovery
/** MUST NOT SIDE-EFFECT! */!
def receiveRecover = {!
case replayedEvent: Event => !
state = updateState(replayedEvent)!
}
re-using updateState, as seen in
receiveCommand
Akka
Persistence
ScalaDays
68. Akka
Persistence
ScalaDays
2014
At-least-once delivery - duplicates
sender destination
$
ok
$
$
$
ok
Re-‐send
69. Akka
Persistence
ScalaDays
2014
M2
At-least-once delivery - unordered
sender destination
M1
ok
1 ok
2
M2
ok
3
M3
M1M3
M2
Re-‐send
70. Akka
Persistence
ScalaDays
2014
M2
At-least-once delivery - crash
sender destination
M1
ok
1 ok
2
M2
ok
3
M3
1. Sent
M1
2. Sent
M2
3. Sent
M3
M3
5.
M2
Confirmed
6.
M3
Confirmed
4.
M1
Confirmed
sender
M1M2
M3
71. PersistentActor with AtLeastOnceDelivery
case class Msg(deliveryId: Long, s: String)
case class Confirm(deliveryId: Long)
sealed trait Evt
case class MsgSent(s: String) extends Evt
case class MsgConfirmed(deliveryId: Long) extends Evt
class Sender(destination: ActorPath)
extends PersistentActor with AtLeastOnceDelivery {
!
def receiveCommand: Receive = {
case s: String => persist(MsgSent(s))(updateState)
case Confirm(deliveryId) => persist(MsgConfirmed(deliveryId))(updateState)
}
!
def receiveRecover: Receive = { case evt: Evt => updateState(evt) }
!
def updateState(evt: Evt): Unit = evt match {
case MsgSent(s) =>
deliver(destination, deliveryId => Msg(deliveryId, s))
!
case MsgConfirmed(deliveryId) => confirmDelivery(deliveryId)
}
}