Professional Documents
Culture Documents
Persistence
Bjrn Antonsson
@bantonsson
Konrad Malawski
@ktosopl
Patrik Nordwall
@patriknw
Reactive Applications
Resilient
Embrace Failure
Failure is a normal part of the application lifecycle
Self Heal
Failure is detected, isolated, and managed
Achievement Unlocked?
Resilient
State is recoverable
Scalable
Append only writes
Something Missing?
Queries
Akka
Persistence
ScalaDays
2014
CQRS
Command Query Responsibility Segregation
CQRS
Separate Models
Command Model
Optimized for command processing
Query Model
Optimized data presentation
Command
Store
Service
Command
Model
Client
Query
Store
Query
Model
PersistentActor
PersistentActor
Replaces:
not persisted
persisted
PersistentActor
var state = S0
!
def processorId = a
!
Command
!
!
Journal
PersistentActor
var state = S0
!
def processorId = a
!
Generate
Events
!
!
Journal
PersistentActor
var state = S0
!
def processorId = a
!
Generate
Events
E1
!
!
Journal
PersistentActor
var state = S0
!
def processorId = a
!
ACK
persisted
E1
!
!
Journal
PersistentActor
var state = S1
!
def processorId = a
!
E1
Apply
event
E1
!
!
Journal
PersistentActor
var state = S1
!
def processorId = a
!
E1
Okey!
E1
!
!
Journal
PersistentActor
var state = S1
!
def processorId = a
!
E1
Okey!
E1
!
!
Journal
PersistentActor
var state = S1
!
def processorId = a
!
E1
Ok, he got my $.
E1
!
!
Journal
PersistentActor
persist(e) { e => }
PersistentActor
def receiveCommand = {!
!
case TakeMy(coins) =>!
persist(BalanceChangedBy(coins)) { changed =>!
state = updateState(changed) !
}!
!
!
async callback
!
!
!
!
}
PersistentActor: persist(){}
def receiveCommand = {!
!
!
!
!
!
!
case GiveMe(coins) if coins <= state.coins =>!
persist(BalanceChangedBy(-coins)) { changed =>!
state = updateState(changed) !
sender() ! TakeMy(coins)!
}!
async callback
}
Safe to mutate
PersistentActor
def receiveCommand = {!
!
!
!
!
!
!
case GiveMe(coins) if coins <= state.coins =>!
persist(BalanceChangedBy(-coins)) { changed =>!
state = updateState(changed) !
sender() ! TakeMy(coins)!
}!
}
Safe to access
sender here
C3
C2
C1
!
def processorId = a
!
E1
!
!
Journal
C3
C2
C1
!
def processorId = a
!
E1
!
!
Journal
C3
C2
C1
!
def processorId = a
!
E1
E2
events get
applied in-order
E1 E2
!
!
Journal
C2
C3
!
def processorId = a
!
E2
E1
E1 E2
!
!
Journal
persistAsync(e) { e => }
persistAsync(e) { e => }
+
defer(e) { e => }
PersistentActor: persistAsync(){}
def receiveCommand = {!
!
!
!
case Mark(id) =>!
sender() ! InitMarking!
persistAsync(Marker) { m =>!
// update state...!
}!
!
!
will NOT force
!
!
}
persistAsync
stashing of commands
PersistentActor: persistAsync(){}
def receiveCommand = {!
!
!
!
case Mark(id) =>!
sender() ! InitMarking!
persistAsync(Marker) { m =>!
// update state...!
}!
!
defer(Marked(id)) { marked =>!
sender() ! marked!
}!
}
NOT persisted
C3
C2
C1
!
def processorId = a
!
!
!
Journal
C2
!
def processorId = a
!
C3
!
!
Journal
C3
!
!
Journal
C3
E1
E2
!
!
Journal
C3
!
def processorId = a
!
E1
E1
E2
!
!
Journal
Akka
Persistence
ScalaDays
E1
E2
E1
E2
E3
!
!
Journal
Akka
Persistence
ScalaDays
e
r
r
e
f
de
M1
M2
var state = S2
!
def processorId = a
!
E1
E2
E1
E2
E3
!
!
Journal
Akka
Persistence
ScalaDays
Recovery
Eventsourced, recovery
Views
Views
Journal
(DB)
Processor
!
def processorId = a
!
!
polling
View
!
def processorId = a
!
!
!
Views
Journal
(DB)
Processor
!
def processorId = a
!
!
!
different ActorPath,
same processorId
!
polling
!
polling
View
!
def processorId = a
!
!
!
View
!
def processorId = a
!
!
!
View
!
def receive = {!
case Persistent(payload, seqNr) =>!
// state += 2 * payload !
!
}!
}
subject to
change!
Views,
as Reactive Streams
View, as ReactiveStream
// Imports ...!
!
import org.reactivestreams.api.Producer!
!
import akka.stream._!
import akka.stream.scaladsl.Flow!
!
import akka.persistence._!
import akka.persistence.stream._!
early preview
pull request
by krasserm
View, as ReactiveStream
early preview
pull request
by krasserm
// 1 producer and 2 consumers:!
val p1: Producer[Persistent] = PersistentFlow.!
fromProcessor(processor-1").!
toProducer(materializer)!
!
Flow(p1).!
foreach(p => println(s"consumer-1: ${p.payload})).!
consume(materializer)!
!
Flow(p1).!
foreach(p => println(s"consumer-2: ${p.payload})).!
consume(materializer)
View, as ReactiveStream
early preview
pull request
by krasserm
// 2 producers (merged) and 1 consumer:!
val p2: Producer[Persistent] = PersistentFlow.!
fromProcessor(processor-2").!
toProducer(materializer)!
Usage in a Cluster
distributed journal (http://akka.io/community/)
Cassandra
DynamoDB
HBase
MongoDB
shared LevelDB journal for testing
single writer
cluster singleton
cluster sharding
Cluster Singleton
A
C
B
D
Cluster Singleton
role: backend-1
role: backend-1
A
B
role: backend-2
role: backend-2
C
D
Cluster Sharding
A
Cluster Sharding
region
node-3
GetShardHome:17
id:17
coordinator
17
->
node2
sender
region
node-1
id:17
region
node-2
Cluster Sharding
region
node-3
coordinator
17
->
node2
sender
region
node-1
id:17
17
->
node2
id:17
region
node-2
id:17
Cluster Sharding
region
node-3
coordinator
17
->
node2
sender
region
node-1
17 -> node2
region
node-2
id:17
17 -> node2
id:17
Akka
Persistence
ScalaDays
2014
17
Cluster Sharding
region
node-3
id:17
coordinator
17
->
node2
sender
region
node-1
17 -> node2
region
node-2
17
->
node2
17
Cluster Sharding
region
node-3
coordinator
17
->
node2
sender
region
node-1
17 -> node2
region
node-2
17
->
node2
17
id:17
Akka
Persistence
ScalaDays
2014
Cluster Sharding
Lost messages
sender
destination
sender
destination
ok
ok
M2
M2 M1
sender
destination
M1
M3
M2
ok
1
ok 3
ok 2
M3
M2
M3 M2 M1
sender
1. Sent
M1
2. Sent
M2
3. Sent
M3
destination
ok 1
ok 2
4.
M1
Confirmed
5.
M2
Confirmed
6.
M3
Confirmed
ok 3
M1
M2
M3
!
!
Next step
Documentation
http://doc.akka.io/docs/akka/2.3.3/scala/persistence.html
http://doc.akka.io/docs/akka/2.3.3/java/persistence.html
http://doc.akka.io/docs/akka/2.3.3/contrib/cluster-sharding.html
Typesafe Activator
https://typesafe.com/activator/template/akka-sample-persistence-scala
https://typesafe.com/activator/template/akka-sample-persistence-java
http://typesafe.com/activator/template/akka-cluster-sharding-scala
Mailing list
http://groups.google.com/group/akka-user