resilient applications with akka persistence - scaladays 2014

73
Resilient Applications with Akka Persistence Patrik Nordwall @patriknw Konrad Malawski @ktosopl Björn Antonsson @bantonsson

Upload: bjoern-antonsson

Post on 27-Aug-2014

5.243 views

Category:

Software


1 download

DESCRIPTION

In this presentation you will learn how to leverage the features introduced in Akka Persistence: opt-in at-least-once delivery semantics between actors and the ability to recover application state after a crash. Both are implemented by storing immutable facts in a persisted append-only log. We will show you how to create persistent actors using command and event sourcing, replicate events with reliable communication, scale out and improve resilience with clustering.

TRANSCRIPT

Page 1: Resilient Applications with Akka Persistence - Scaladays 2014

Resilient Applications with Akka Persistence

Patrik Nordwall@patriknw

Konrad Malawski@ktosopl

Björn Antonsson@bantonsson

Page 2: Resilient Applications with Akka Persistence - Scaladays 2014

Reactive Applications

Akka  Persistence  ScalaDays  2014

Page 3: Resilient Applications with Akka Persistence - Scaladays 2014

Resilient

• Embrace Failure • Failure is a normal part of the application lifecycle

• Self Heal • Failure is detected, isolated, and managed

Akka  Persistence  ScalaDays  2014

Page 4: Resilient Applications with Akka Persistence - Scaladays 2014

The Naïve Way

• Write State to Database

• Transactions Everywhere

• Problem Solved?

• Not Scalable, Responsive, Event-Driven!

Akka  Persistence  ScalaDays  2014

Page 5: Resilient Applications with Akka Persistence - Scaladays 2014

Command and Event Sourcing

Page 6: Resilient Applications with Akka Persistence - Scaladays 2014

Command and Event Sourcing

• State is the sum of Events

• Events are persisted to Store

• Append only

• Scales well

Akka  Persistence  ScalaDays  2014

Page 7: Resilient Applications with Akka Persistence - Scaladays 2014

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

Page 8: Resilient Applications with Akka Persistence - Scaladays 2014

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

Page 9: Resilient Applications with Akka Persistence - Scaladays 2014

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

Page 10: Resilient Applications with Akka Persistence - Scaladays 2014

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

Page 11: Resilient Applications with Akka Persistence - Scaladays 2014

Achievement Unlocked?

• Resilient

• State is recoverable

• Scalable

• Append only writes

• Something Missing?

• Queries

Akka  Persistence  ScalaDays  2014

Page 12: Resilient Applications with Akka Persistence - Scaladays 2014

CQRSCommand Query Responsibility Segregation

Page 13: Resilient Applications with Akka Persistence - Scaladays 2014

CQRS

• Separate Models

• Command Model

• Optimized for command processing

• Query Model

• Optimized data presentation

Akka  Persistence  ScalaDays  2014

Page 14: Resilient Applications with Akka Persistence - Scaladays 2014

Query Model from Events

• Source the Events

• Pick what fits

• In Memory

• SQL Database

• Graph Database

• Key Value Store

Akka  Persistence  ScalaDays  2014

Page 15: Resilient Applications with Akka Persistence - Scaladays 2014

Akka  Persistence  ScalaDays  2014

Client

Service

Query  Model

Command Store

Query Store

Command  Model

Page 16: Resilient Applications with Akka Persistence - Scaladays 2014

PersistentActor

Akka  Persistence  ScalaDays  2014

Page 17: Resilient Applications with Akka Persistence - Scaladays 2014

PersistentActor

Processor & Eventsourced ProcessorReplaces:

in Akka 2.3.4+

Page 18: Resilient Applications with Akka Persistence - Scaladays 2014

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

Page 19: Resilient Applications with Akka Persistence - Scaladays 2014

var state = S0 !

def processorId = “a” !

PersistentActor

Command

!

!

Journal

Page 20: Resilient Applications with Akka Persistence - Scaladays 2014

PersistentActor

var state = S0 !

def processorId = “a” !

!

!

Journal

Generate Events

Page 21: Resilient Applications with Akka Persistence - Scaladays 2014

PersistentActor

var state = S0 !

def processorId = “a” !

!

!

Journal

Generate Events

E1

Page 22: Resilient Applications with Akka Persistence - Scaladays 2014

PersistentActor

ACK “persisted”

!

!

Journal

E1

var state = S0 !

def processorId = “a” !

Page 23: Resilient Applications with Akka Persistence - Scaladays 2014

PersistentActor

“Apply” event

!

!

Journal

E1

var state = S1 !

def processorId = “a” !

E1

Page 24: Resilient Applications with Akka Persistence - Scaladays 2014

PersistentActor

!

!

Journal

E1

var state = S1 !

def processorId = “a” !

E1

Okey!

Page 25: Resilient Applications with Akka Persistence - Scaladays 2014

PersistentActor

!

!

Journal

E1

var state = S1 !

def processorId = “a” !

E1

Okey!

Page 26: Resilient Applications with Akka Persistence - Scaladays 2014

PersistentActor

!

!

Journal

E1

var state = S1 !

def processorId = “a” !

E1

Ok, he got my $.

Page 27: Resilient Applications with Akka Persistence - Scaladays 2014

PersistentActor

class BitCoinWallet extends PersistentActor {!! var state = Wallet(coins = 0)!! def updateState(e: Event): State = {! case BalanceChangedBy(coins) => state.updatedWith(coins)! }! ! // API:!! def receiveCommand = ??? // TODO!! def receiveRecover = ??? // TODO!!}!

Page 28: Resilient Applications with Akka Persistence - Scaladays 2014

persist(e) { e => }

Page 29: Resilient Applications with Akka Persistence - Scaladays 2014

PersistentActor

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

async callback

Page 30: Resilient Applications with Akka Persistence - Scaladays 2014

PersistentActor: persist(){}

def receiveCommand = {!!!!!!! case GiveMe(coins) if coins <= state.coins =>! persist(BalanceChangedBy(-coins)) { changed =>! state = updateState(changed) ! sender() ! TakeMy(coins)! }!}

async callbackSafe to mutate

the Actor’s state

Page 31: Resilient Applications with Akka Persistence - Scaladays 2014

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

Page 32: Resilient Applications with Akka Persistence - Scaladays 2014

persist(){} - Ordering guarantees

!

!

Journal

E1

var state = S0 !

def processorId = “a” !

C1C2

C3

Page 33: Resilient Applications with Akka Persistence - Scaladays 2014

!

!

Journal

E1

var state = S0 !

def processorId = “a” !

C1C2

C3

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

persist(){} - Ordering guarantees

Page 34: Resilient Applications with Akka Persistence - Scaladays 2014

!

!

Journal

var state = S0 !

def processorId = “a” !

C1C2

C3 E1

E2

E2E1

events get applied in-order

persist(){} - Ordering guarantees

Page 35: Resilient Applications with Akka Persistence - Scaladays 2014

C2

!

!

Journal

var state = S0 !

def processorId = “a” !

C3 E1 E2

E2E1

and the cycle repeats

persist(){} - Ordering guarantees

Page 36: Resilient Applications with Akka Persistence - Scaladays 2014

persistAsync(e) { e => }

Page 37: Resilient Applications with Akka Persistence - Scaladays 2014

persistAsync(e) { e => } + defer(e) { e => }

Page 38: Resilient Applications with Akka Persistence - Scaladays 2014

def receiveCommand = {!!!! case Mark(id) =>! sender() ! InitMarking! persistAsync(Marker) { m =>! // update state...! }!!!!!}

persistAsync

PersistentActor: persistAsync(){}

will NOT force stashing of commands

Page 39: Resilient Applications with Akka Persistence - Scaladays 2014

PersistentActor: persistAsync(){}

def receiveCommand = {!!!! case Mark(id) =>! sender() ! InitMarking! persistAsync(Marker) { m =>! // update state...! }!! defer(Marked(id)) { marked =>! sender() ! marked! }!}

execute once all persistAsync handlers done

NOT persisted

Page 40: Resilient Applications with Akka Persistence - Scaladays 2014

persistAsync(){} - Ordering guarantees

!

!

Journal

var state = S0 !

def processorId = “a” !

C1C2

C3

Page 41: Resilient Applications with Akka Persistence - Scaladays 2014

persistAsync(){} - Ordering guarantees

!

!

Journal

var state = S0 !

def processorId = “a” !C2

C3

Page 42: Resilient Applications with Akka Persistence - Scaladays 2014

persistAsync(){} - Ordering guarantees

!

!

Journal

var state = S0 !

def processorId = “a” !

C3

Page 43: Resilient Applications with Akka Persistence - Scaladays 2014

persistAsync(){} - Ordering guarantees

!

!

Journal

var state = S0 !

def processorId = “a” !

C3

E1

E2

Page 44: Resilient Applications with Akka Persistence - Scaladays 2014

persistAsync(){} - Ordering guarantees

var state = S0 !

def processorId = “a” !

C3

E1

Akka  Persistence  ScalaDays

!

!

Journal

E1

E2

Page 45: Resilient Applications with Akka Persistence - Scaladays 2014

persistAsync(){} - Ordering guarantees

E1

var state = S1 !

def processorId = “a” !

E2

E1

E2

!

!

JournalAkka  Persistence  ScalaDays

E2

E3E1

Page 46: Resilient Applications with Akka Persistence - Scaladays 2014

persistAsync(){} - Ordering guarantees

E1

var state = S2 !

def processorId = “a” !

E2

E1 E2

deferred handlers triggered

M1M2

!

!

JournalAkka  Persistence  ScalaDays

E2

E3E1

Page 47: Resilient Applications with Akka Persistence - Scaladays 2014

Recovery

Akka  Persistence  ScalaDays

Page 48: Resilient Applications with Akka Persistence - Scaladays 2014

Eventsourced, recovery

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

re-using updateState, as seen in receiveCommand

Akka  Persistence  ScalaDays

Page 49: Resilient Applications with Akka Persistence - Scaladays 2014

Views

Akka  Persistence  ScalaDays

Page 50: Resilient Applications with Akka Persistence - Scaladays 2014

Journal (DB)

!

!

!

Views

!Processor

!def processorId = “a”

!

polling

Akka  Persistence  ScalaDays

!View

!def processorId = “a”

!!!

Page 51: Resilient Applications with Akka Persistence - Scaladays 2014

Journal (DB)

!

!

!

Views

!Processor

!def processorId = “a”

!

polling

!View

!def processorId = “a”

!!!

polling

different ActorPath, same processorId

Akka  Persistence  ScalaDays

!View

!def processorId = “a”

!!!

Page 52: Resilient Applications with Akka Persistence - Scaladays 2014

View

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

subject to change!

Akka  Persistence  ScalaDays

Page 53: Resilient Applications with Akka Persistence - Scaladays 2014

Views, as Reactive Streams

Akka  Persistence  ScalaDays

Page 54: Resilient Applications with Akka Persistence - Scaladays 2014

View, as ReactiveStream

// Imports ...!!import org.reactivestreams.api.Producer!!import akka.stream._!import akka.stream.scaladsl.Flow!!import akka.persistence._!import akka.persistence.stream._!

val materializer = FlowMaterializer(MaterializerSettings())!

pull request by krasserm

early preview

Akka  Persistence  ScalaDays

Page 55: Resilient Applications with Akka Persistence - Scaladays 2014

View, as ReactiveStream

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

pull request by krasserm

early preview

Akka  Persistence  ScalaDays

Page 56: Resilient Applications with Akka Persistence - Scaladays 2014

View, as ReactiveStream

// 2 producers (merged) and 1 consumer:!val p2: Producer[Persistent] = PersistentFlow.! fromProcessor(“processor-2").! toProducer(materializer)!val p3: Producer[Persistent] = PersistentFlow.! fromProcessor(“processor-3").! toProducer(materializer)!!Flow(p2).merge(p3). // triggers on “either”! foreach { p => println(s"consumer-3: ${p.payload}") }.! consume(materializer)!

pull request by krasserm

early preview

Akka  Persistence  ScalaDays

Page 57: Resilient Applications with Akka Persistence - Scaladays 2014

Akka  Persistence  ScalaDays  2014

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

Page 58: Resilient Applications with Akka Persistence - Scaladays 2014

Akka  Persistence  ScalaDays  2014

Cluster Singleton

AB

C D

Page 59: Resilient Applications with Akka Persistence - Scaladays 2014

Akka  Persistence  ScalaDays  2014

Cluster Singleton

AB

C

D

role: backend-1 role: backend-1

role: backend-2 role: backend-2

Page 60: Resilient Applications with Akka Persistence - Scaladays 2014

Akka  Persistence  ScalaDays  2014

Cluster Sharding

A B

C D

Page 61: Resilient Applications with Akka Persistence - Scaladays 2014

Akka  Persistence  ScalaDays  2014

Cluster Sharding

sender

id:17

region node-­‐1

coordinator

region node-­‐2

region node-­‐3

GetShardHome:17

id:17 ShardHome:17  -­‐>  node2

17  -­‐>  node2

Page 62: Resilient Applications with Akka Persistence - Scaladays 2014

Akka  Persistence  ScalaDays  2014

Cluster Sharding

sender region node-­‐1

coordinator

region node-­‐2

region node-­‐3

id:17

id:17GetShardHome:17

ShardHome:17  -­‐>  node2

id:17

17  -­‐>  node2

17  -­‐>  node2

Page 63: Resilient Applications with Akka Persistence - Scaladays 2014

Akka  Persistence  ScalaDays  2014

Cluster Sharding

17

sender region node-­‐1

coordinator

region node-­‐2

region node-­‐3

id:17

id:17

17  -­‐>  node2

17  -­‐>  node2

17  -­‐>  node2

Page 64: Resilient Applications with Akka Persistence - Scaladays 2014

Akka  Persistence  ScalaDays  2014

Cluster Sharding

17

sender region node-­‐1

coordinator

region node-­‐2

region node-­‐3

17  -­‐>  node2

17  -­‐>  node2

17  -­‐>  node2

id:17

Page 65: Resilient Applications with Akka Persistence - Scaladays 2014

Akka  Persistence  ScalaDays  2014

Cluster Sharding

17

sender region node-­‐1

coordinator

region node-­‐2

region node-­‐3

17  -­‐>  node2

17  -­‐>  node2

17  -­‐>  node2

id:17

Page 66: Resilient Applications with Akka Persistence - Scaladays 2014

Cluster Sharding

val idExtractor: ShardRegion.IdExtractor = { case cmd: Command => (cmd.postId, cmd) } ! val shardResolver: ShardRegion.ShardResolver = msg => msg match { case cmd: Command => (math.abs(cmd.postId.hashCode) % 100).toString }

ClusterSharding(system).start( typeName = BlogPost.shardName, entryProps = Some(BlogPost.props()), idExtractor = BlogPost.idExtractor, shardResolver = BlogPost.shardResolver)

val blogPostRegion: ActorRef = ClusterSharding(context.system).shardRegion(BlogPost.shardName) !val postId = UUID.randomUUID().toString blogPostRegion ! BlogPost.AddPost(postId, author, title)

Page 67: Resilient Applications with Akka Persistence - Scaladays 2014

Akka  Persistence  ScalaDays  2014

Lost messages

sender destination

$

Page 68: Resilient Applications with Akka Persistence - Scaladays 2014

Akka  Persistence  ScalaDays  2014

At-least-once delivery - duplicates

sender destination

$

ok

$

$$

ok

Re-­‐send

Page 69: Resilient Applications with Akka Persistence - Scaladays 2014

Akka  Persistence  ScalaDays  2014

M2

At-least-once delivery - unordered

sender destination

M1

ok  1 ok  2

M2

ok  3

M3

M1M3M2

Re-­‐send

Page 70: Resilient Applications with Akka Persistence - Scaladays 2014

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

senderM1M2

M3

Page 71: Resilient Applications with Akka Persistence - Scaladays 2014

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

Page 72: Resilient Applications with Akka Persistence - Scaladays 2014

Akka  Persistence  ScalaDays  2014

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

• Migration guide from Eventsourced • http://doc.akka.io/docs/akka/2.3.3/project/migration-guide-eventsourced-2.3.x.html

Page 73: Resilient Applications with Akka Persistence - Scaladays 2014

©Typesafe 2014 – All Rights Reserved