Switch branches/tags
2.5.12_2.13.0-M3 2.5.13-release-2.13.0-M3 2.5.14_2.13.0-M3 2.5.15_2.13.0-M3 2.5.16_2.13.0-M3 2.5.17_2.13.0-M3 25047/simplify-test actorRefWithAckDocTest akka-typed-javadsl-behaviour-build-higherorder arteryTests asyncDnsRetryOverTcpWhenTruncated camel-java9 convertedReference doc-remove-tracking-2 findFreeUdpPort fixLagomLink integrate-docs-scala-java issue-23926-remote-watch javaAndScalaUnidoc jdk9-build jr/w/gce-benchmark-setup jr/26104-fix-SSLEngine-IllegalStateException-in-TlsActor ktoso-patch-1 leavingClusterEventLogs master mavenUpperCase multiJvmShardingLogTest must raboof-patch-1 release-1.1.3 release-1.1.4 release-1.2 release-1.3 release-1.3.1 release-2.0 release-2.1 release-2.2 release-2.3 release-2.4-http release-2.4 releaseFromTravis releaseWithJdk9ForJdk8 releasing- removeAkkaSslConfig revert-21064-wip-http-javadsl-package-alignment-johanandren revert-24689-unidoc-cluster-richard sbtWhitesourceIssue20 scala-2.13.0-M4 scala-2.13.0-M5 scalafix stream-http-2.0 streamConverters streamReference tcp-stream-termination-22163 testPrValidation typedPersistentStashingActor unidoc2 v2.5.6-preparations v2.5.7-preparations v2.5.8-preparations v2.5.19-scala-2.13.0-M5 wip-19623-subsource-cannot-push-twice-johanandren wip-19964-broken-link-WebSocket-example-RK wip-20984-typed-reliable-delivery-poc-patriknw wip-21624-SendQueue-debug-patriknw wip-23207-log-artery-dropped-patriknw wip-23593-RestartSpec-patriknw wip-23770-req-resp-patriknw wip-24113-cs-cs-chbatey wip-24155-jackson-patriknw wip-24980-debug-patriknw wip-25072-optimized-recovery-patriknw wip-25072-recovery-first-patriknw wip-25482-then-reply-patriknw wip-25484-enclosing-class-patriknw wip-25485-raffle-sample-patriknw wip-25717-persistence-stash-patriknw wip-25950-allObservers-patriknw wip-26012-rebalance-patriknw wip-ThisActorSystemQuarantinedEvent-patriknw wip-Typed-Session-RK wip-auto-confirmation-patriknw wip-conflate-example wip-connection-issue-patriknw wip-debug-LargeMessageClusterSpec-patriknw wip-debug-tests wip-experiments-√ wip-gcloud-faninout-2m wip-jdk-9-release-8-2m wip-jdk9-build-2m wip-lanes-debug-patriknw wip-managed-blocking-for-tpe-√ wip-misc-doc-fixes-RK wip-persistence-typed-impl-patriknw wip-persistenceId-patriknw wip-rm-ActorRef-Future-patriknw wip-rolling-artery-patriknw wip-rp-docs wip-scala-2.13-2m wip-streams-improvements-√ wip-tol-latest-api-mappings-plugin-2m wip-tol-latest-api-mappings-plugin wip-tol-no-tests-2m wip-typed-Java8-∂π wip-validate-pull-request-sbt-1.0-2m
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
124 lines (79 sloc) 5.46 KB

Persistent FSM


Persistent FSMs are part of Akka persistence, you must add the following dependency in your project:

@@dependency[sbt,Maven,Gradle] { group="com.typesafe.akka" artifact="akka-persistence_$scala.binary_version$" version="$akka.version$" }

Persistent FSM

@@@ warning

Persistent FSM is no longer actively developed and will be replaced by @refAkka Typed Persistence. It is not advised to build new applications with Persistent FSM.


@scala[PersistentFSM]@java[AbstractPersistentFSM] handles the incoming messages in an FSM like fashion. Its internal state is persisted as a sequence of changes, later referred to as domain events. Relationship between incoming messages, FSM's states and transitions, persistence of domain events is defined by a DSL.

A Simple Example

To demonstrate the features of the @scala[PersistentFSM trait]@java[AbstractPersistentFSM], consider an actor which represents a Web store customer. The contract of our "WebStoreCustomerFSMActor" is that it accepts the following commands:

Scala : @@snip PersistentFSMSpec.scala { #customer-commands }

Java : @@snip { #customer-commands }

AddItem sent when the customer adds an item to a shopping cart Buy - when the customer finishes the purchase Leave - when the customer leaves the store without purchasing anything GetCurrentCart allows to query the current state of customer's shopping cart

The customer can be in one of the following states:

Scala : @@snip PersistentFSMSpec.scala { #customer-states }

Java : @@snip { #customer-states }

LookingAround customer is browsing the site, but hasn't added anything to the shopping cart Shopping customer has recently added items to the shopping cart Inactive customer has items in the shopping cart, but hasn't added anything recently Paid customer has purchased the items

@@@ note

@scala[PersistentFSM]@java[AbstractPersistentFSM] states must @scala[inherit from trait]@java[implement interface] PersistentFSM.FSMState and implement the @scala[def identifier: String]@java[String identifier()] method. This is required in order to simplify the serialization of FSM states. String identifiers should be unique!


Customer's actions are "recorded" as a sequence of "domain events" which are persisted. Those events are replayed on an actor's start in order to restore the latest customer's state:

Scala : @@snip PersistentFSMSpec.scala { #customer-domain-events }

Java : @@snip { #customer-domain-events }

Customer state data represents the items in a customer's shopping cart:

Scala : @@snip PersistentFSMSpec.scala { #customer-states-data }

Java : @@snip { #customer-states-data }

Here is how everything is wired together:

Scala : @@snip PersistentFSMSpec.scala { #customer-fsm-body }

Java : @@snip { #customer-fsm-body }

@@@ note

State data can only be modified directly on initialization. Later it's modified only as a result of applying domain events. Override the applyEvent method to define how state data is affected by domain events, see the example below


Scala : @@snip PersistentFSMSpec.scala { #customer-apply-event }

Java : @@snip { #customer-apply-event }

andThen can be used to define actions which will be executed following event's persistence - convenient for "side effects" like sending a message or logging. Notice that actions defined in andThen block are not executed on recovery:

Scala : @@snip PersistentFSMSpec.scala { #customer-andthen-example }

Java : @@snip { #customer-andthen-example }

A snapshot of state data can be persisted by calling the saveStateSnapshot() method:

Scala : @@snip PersistentFSMSpec.scala { #customer-snapshot-example }

Java : @@snip { #customer-snapshot-example }

On recovery state data is initialized according to the latest available snapshot, then the remaining domain events are replayed, triggering the applyEvent method.