Skip to content

Latest commit

 

History

History
85 lines (58 loc) · 4.77 KB

persistent-fsm.md

File metadata and controls

85 lines (58 loc) · 4.77 KB
uid title
persistent-fsm
Persistence FSM

Persistence FSM

PersistentFSM 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 PersistentFSM class, consider an actor which represents a Web store customer. The contract of our "WebStoreCustomerFSMActor" is that it accepts the following commands:

[!code-csharpWebStoreCustomerFSMActor.cs]

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: [!code-csharpWebStoreCustomerFSMActor.cs]

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

PersistentFSM states must inherit from trait PersistentFSM.IFsmState and implement the string Identifier property. 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:

[!code-csharpWebStoreCustomerFSMActor.cs]

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

[!code-csharpWebStoreCustomerFSMActor.cs]

Side-effects:

[!code-csharpWebStoreCustomerFSMActor.cs]

Here is how everything is wired together: [!code-csharpWebStoreCustomerFSMActor.cs]

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

[!code-csharpWebStoreCustomerFSMActor.cs]

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:

GoTo(Paid.Instance).Applying(OrderExecuted.Instance).AndThen(cart =>
{
    if (cart is NonEmptyShoppingCart nonShoppingCart)
    {
        reportActor.Tell(new PurchaseWasMade(nonShoppingCart.Items));
    }
});

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

Stop().Applying(OrderDiscarded.Instance).AndThen(cart =>
{
    reportActor.Tell(ShoppingCardDiscarded.Instance);
    SaveStateSnapshot();
});

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

Periodical Snapshot by Snapshot-After

You can enable periodical SaveStateSnapshot() calls in PersistentFSM if you turn the following flag on in reference.conf

akka.persistence.fsm.snapshot-after = 1000

this means SaveStateSnapshot() is called after the sequence number reaches multiple of 1000.

Note

SaveStateSnapshot() might not be called exactly at sequence numbers being multiple of the snapshot-after configuration value. This is because PersistentFSM works in a sort of "batch" mode when processing and persisting events, and SaveStateSnapshot() is called only at the end of the "batch". For example, if you set akka.persistence.fsm.snapshot-after = 1000, it is possible that SaveStateSnapshot() is called at lastSequenceNr = 1005, 2003, ... A single batch might persist state transition, also there could be multiple domain events to be persisted if you pass them to Applying method in the PersistentFSM DSL.