This is a study note of the Akka persistence. It is based on the Akka persistence documents.

Introduction

An event sourced actor, also known as a persistent actor, uses persistence to recover its state from restart, by a supervisor or a manual stop-start, or migratioin within a cluster. Events, not the current state of the actor, are persisted by event sourcing. States are recovered by event replaying of the full history or from a snapshot.

Basic Activities

An event sourced actor is an instance of EeventSourcedBehavior that has at least four fields: a PersistenceId instance, an emptyState, a commandHandler and an eventHandler. The received messages are commands. The typical activities are:

  • Intially, an even sourced actor is created with an emptyState.
  • When receiving a command, the commandHandler returns an Effect, often by calling Effect.persist(event) to persist an event. A successful persistence triggers the eventHandler.
  • The eventHandler is called either after a successful persistence or after a event replaying when the actor restarts. The eventHandler takes the current state and the event and produces a new state.

PersistenceId

The PersistenceId is the unique id in event journal and snapstore. It is often used with Cluster Sharding to ensure there is only one active entity for each PersistenceId(entityId). Here the entityId is the business domain identifier of the entity. But the entityId might not be unique for different types of entities and should include the entity type to be unique. Cluster sharding uses EntityTypeKey.name and PersistenceId.apply() method to constructe a unique persistence id from a entityTypeHint|entityId where the | is the default separator.

Command Handler and Effects

A command handler takes current State and the incoming Command to produce an Effect directive that defines what event or events, if any, to persist. Effects are created using one of the Effect factory methods from the following list:

  • persist: persist one or more events atomically.
  • none: no event to be persisted.
  • unhandled: not supported in current state.
  • stop: stop this actor.
  • stash: stash the current command.
  • unstashAll: process the commands sinces the last stash.
  • reply: send a reply message to an ActorRef.

Effects can be chained by Effect.persist(...).thenRun... to run side effects after the primary effect. The thenRun call register callbacks for side effects and uses the new state, i.e., the state after event handler is called with the previous state and new event. In place of thenRun, you can call thenStop, thenUnstashAll, thenReply and thenNoReply.

Side effect are executed sequentially on an at-most-once basis and will be skipped if the persist fails. However, when run it in the RecoveryCompleted signal handler, a side effect may run more than once.

If database doesn’t support atomic persistence of multiple events, an EventRejectedExecption is throwed and can be handled with a supervior.

Event Handler

When an event has been persisted successfully, an event handler applies the event to the current state. The same event hanlder is used when the actor recovers the current state after restarting. The event handler must only update the actor state (ideally, an immutable type) and never perform side effects. Side effects, such as sending a message or stoping the actor etc., should only be performed in thenRun/thenStop/thenUnstashAll from the command handler after event persistence.

During recovery, only perform side effects from the RecoveryCompleted singal handler after recovery.

Changing Behavior

A regular actor processes a new message and returns a new Behavior used for next message. However, the persistent actor needs to recover its state from database by replaying events after an auto restart or a manual stop-start.

In persistent actor, both command handler and event handler takes a state parameter and can act on the command and event based on the current state.

Request-Response Pattern

This pattern is common because the status of command often matters to the sender. The EventSourcedBehavior.withEnforcedReplies(...) method can be used to return ReplayEffect that is created with Effect.reply, Effect.noReply, Effect.thenReply and Effect.thenNoReply. The command must have a field of ActorRef[ReplyMessageType].

The Effect.noReply and Effect.thenNoReply are used to explicitly mean no reply for specific commands or send reply lately.

Stash

When persisting events or recoverying from restart, commands are stashed automatcially until persistency or recovery is completed.

Sometimes you may want to stash commands to defere processing them when some conditions are meet.

Stashed messages are kept in memory and may be lost in restart or migration.