2019-09-30 06:38:26 -07:00
---
2020-12-07 08:41:43 +01:00
project.description: Akka Persistence Classic, Event Sourcing with Akka, At-Least-Once delivery, snapshots, recovery and replay with Akka actors.
2019-09-30 06:38:26 -07:00
---
2019-07-05 08:21:38 +02:00
# Classic Persistence
2019-09-03 13:23:47 -07:00
@@include [includes.md ](includes.md ) { #actor -api }
2020-01-07 14:01:11 +01:00
For the full documentation of this feature and for new projects see @ref: [Event Sourcing ](typed/persistence.md ).
2019-07-05 08:21:38 +02:00
2019-11-05 20:06:32 +01:00
## Module info
2013-09-14 14:19:18 +02:00
2018-05-15 18:44:33 +09:00
To use Akka Persistence, you must add the following dependency in your project:
2013-09-14 14:19:18 +02:00
2018-01-31 17:19:19 +01:00
@@dependency [sbt,Maven,Gradle] {
2020-12-04 13:26:42 +01:00
bomGroup=com.typesafe.akka bomArtifact=akka-bom_$scala.binary.version$ bomVersionSymbols=AkkaVersion
2020-06-25 11:59:37 +02:00
symbol1=AkkaVersion
value1="$akka.version$"
2018-01-31 17:19:19 +01:00
group="com.typesafe.akka"
2020-05-06 11:06:55 +02:00
artifact="akka-persistence_$scala.binary.version$"
2020-06-25 11:59:37 +02:00
version=AkkaVersion
2020-12-04 13:26:42 +01:00
group2="com.typesafe.akka"
artifact2="akka-persistence-testkit_$scala.binary.version$"
version2=AkkaVersion
scope2=test
2018-01-31 17:19:19 +01:00
}
2013-09-14 14:19:18 +02:00
2019-09-26 22:48:12 +02:00
You also have to select journal plugin and optionally snapshot store plugin, see
@ref: [Persistence Plugins ](persistence-plugins.md ).
2017-05-10 16:20:38 +02:00
2019-11-05 20:06:32 +01:00
@@project -info{ projectId="akka-persistence" }
2018-05-15 18:44:33 +09:00
## Introduction
2019-09-26 22:48:12 +02:00
See introduction in @ref: [Persistence ](typed/persistence.md#introduction )
2018-06-15 14:25:46 +02:00
2019-09-26 22:48:12 +02:00
Akka Persistence also provides point-to-point communication with at-least-once message delivery semantics.
2018-06-15 14:25:46 +02:00
2019-09-26 22:48:12 +02:00
### Architecture
2017-05-10 16:20:38 +02:00
2017-06-26 18:15:43 +09:00
* @scala [`PersistentActor` ]@java [`AbstractPersistentActor` ]: Is a persistent, stateful actor. It is able to persist events to a journal and can react to
2017-05-10 16:20:38 +02:00
them in a thread-safe manner. It can be used to implement both *command* as well as *event sourced* actors.
When a persistent actor is started or restarted, journaled messages are replayed to that actor so that it can
2018-11-13 19:19:04 +00:00
recover its state from these messages.
2017-06-26 18:15:43 +09:00
* @scala [`AtLeastOnceDelivery` ]@java [`AbstractPersistentActorAtLeastOnceDelivery` ]: To send messages with at-least-once delivery semantics to destinations, also in
2017-05-10 16:20:38 +02:00
case of sender and receiver JVM crashes.
2017-06-26 18:15:43 +09:00
* `AsyncWriteJournal` : A journal stores the sequence of messages sent to a persistent actor. An application can control which messages
are journaled and which are received by the persistent actor without being journaled. Journal maintains `highestSequenceNr` that is increased on each message.
2021-06-02 13:37:52 +02:00
The storage backend of a journal is pluggable.
2020-03-11 15:56:22 +01:00
Replicated journals are available as [Community plugins ](https://akka.io/community/ ).
2018-11-13 19:19:04 +00:00
* *Snapshot store*: A snapshot store persists snapshots of a persistent actor's state. Snapshots are
2017-05-10 16:20:38 +02:00
used for optimizing recovery times. The storage backend of a snapshot store is pluggable.
2020-03-11 15:56:22 +01:00
The persistence extension comes with a "local" snapshot storage plugin, which writes to the local filesystem. Replicated snapshot stores are available as [Community plugins ](https://akka.io/community/ )
2020-12-07 08:41:43 +01:00
* *Event Sourcing*. Based on the building blocks described above, Akka persistence provides abstractions for the
development of event sourced applications (see section @ref: [Event Sourcing ](typed/persistence.md#event-sourcing-concepts )).
2014-06-25 12:51:21 +02:00
2019-09-26 22:48:12 +02:00
## Example
2018-01-10 11:12:56 +09:00
2020-12-07 08:41:43 +01:00
Akka persistence supports Event Sourcing with the @scala [`PersistentActor` trait]@java [`AbstractPersistentActor` abstract class]. An actor that extends this @scala [trait]@java [class] uses the
2017-06-26 18:15:43 +09:00
`persist` method to persist and handle events. The behavior of @scala [a `PersistentActor` ]@java [an `AbstractPersistentActor` ]
is defined by implementing @scala [`receiveRecover` ]@java [`createReceiveRecover` ] and @scala [`receiveCommand` ]@java [`createReceive` ]. This is demonstrated in the following example.
Scala
2018-09-13 10:49:51 +02:00
: @@snip [PersistentActorExample.scala ](/akka-docs/src/test/scala/docs/persistence/PersistentActorExample.scala ) { #persistent -actor-example }
2014-06-25 12:51:21 +02:00
2017-06-26 18:15:43 +09:00
Java
2018-09-13 10:49:51 +02:00
: @@snip [PersistentActorExample.java ](/akka-docs/src/test/java/jdocs/persistence/PersistentActorExample.java ) { #persistent -actor-example }
2014-06-25 12:51:21 +02:00
2017-05-10 16:20:38 +02:00
The example defines two data types, `Cmd` and `Evt` to represent commands and events, respectively. The
`state` of the `ExamplePersistentActor` is a list of persisted event data contained in `ExampleState` .
2014-06-25 12:51:21 +02:00
2017-06-26 18:15:43 +09:00
The persistent actor's @scala [`receiveRecover` ]@java [`createReceiveRecover` ] method defines how `state` is updated during recovery by handling `Evt`
and `SnapshotOffer` messages. The persistent actor's @scala [`receiveCommand` ]@java [`createReceive` ] method is a command handler. In this example,
2017-05-09 09:48:57 +09:00
a command is handled by generating an event which is then persisted and handled. Events are persisted by calling
2017-05-10 16:20:38 +02:00
`persist` with an event (or a sequence of events) as first argument and an event handler as second argument.
2014-06-25 12:51:21 +02:00
2017-05-10 16:20:38 +02:00
The `persist` method persists events asynchronously and the event handler is executed for successfully persisted
2014-06-25 12:51:21 +02:00
events. Successfully persisted events are internally sent back to the persistent actor as individual messages that trigger
event handler executions. An event handler may close over persistent actor state and mutate it. The sender of a persisted
event is the sender of the corresponding command. This allows event handlers to reply to the sender of a command
(not shown).
The main responsibility of an event handler is changing persistent actor state using event data and notifying others
about successful state changes by publishing events.
2017-05-10 16:20:38 +02:00
When persisting events with `persist` it is guaranteed that the persistent actor will not receive further commands between
the `persist` call and the execution(s) of the associated event handler. This also holds for multiple `persist`
2019-10-01 15:29:12 +02:00
calls in context of a single command. Incoming messages are @ref: [stashed ](#internal-stash ) until the `persist`
2017-03-31 13:52:05 +03:00
is completed.
2014-06-25 12:51:21 +02:00
2017-05-10 16:20:38 +02:00
If persistence of an event fails, `onPersistFailure` will be invoked (logging the error by default),
2015-06-23 21:01:36 +02:00
and the actor will unconditionally be stopped. If persistence of an event is rejected before it is
2017-05-10 16:20:38 +02:00
stored, e.g. due to serialization error, `onPersistRejected` will be invoked (logging a warning
2015-11-04 13:49:30 +01:00
by default) and the actor continues with the next message.
2014-12-14 21:45:22 +01:00
2017-05-10 16:20:38 +02:00
@@@ note
2014-06-25 12:51:21 +02:00
2017-05-10 16:20:38 +02:00
It's also possible to switch between different command handlers during normal processing and recovery
2017-06-26 18:15:43 +09:00
with @scala [`context.become()` ]@java [`getContext().become()` ] and @scala [`context.unbecome()` ]@java [`getContext().unbecome()` ]. To get the actor into the same state after
2017-05-10 16:20:38 +02:00
recovery you need to take special care to perform the same state transitions with `become` and
2017-06-26 18:15:43 +09:00
`unbecome` in the @scala [`receiveRecover` ]@java [`createReceiveRecover` ] method as you would have done in the command handler.
Note that when using `become` from @scala [`receiveRecover` ]@java [`createReceiveRecover` ] it will still only use the @scala [`receiveRecover` ]@java [`createReceiveRecover` ]
2017-05-10 16:20:38 +02:00
behavior when replaying the events. When replay is completed it will use the new behavior.
2014-05-21 01:35:21 +02:00
2017-05-10 16:20:38 +02:00
@@@
2016-09-12 23:18:06 +09:00
2017-05-11 17:27:57 +02:00
< a id = "persistence-id" > < / a >
2017-05-10 16:20:38 +02:00
### Identifiers
2014-06-25 12:51:21 +02:00
2014-06-26 13:56:01 +02:00
A persistent actor must have an identifier that doesn't change across different actor incarnations.
2017-05-10 16:20:38 +02:00
The identifier must be defined with the `persistenceId` method.
2017-06-26 18:15:43 +09:00
Scala
2018-09-13 10:49:51 +02:00
: @@snip [PersistenceDocSpec.scala ](/akka-docs/src/test/scala/docs/persistence/PersistenceDocSpec.scala ) { #persistence -id-override }
2017-06-26 18:15:43 +09:00
Java
2018-09-13 10:49:51 +02:00
: @@snip [LambdaPersistenceDocTest.java ](/akka-docs/src/test/java/jdocs/persistence/LambdaPersistenceDocTest.java ) { #persistence -id-override }
2013-10-08 11:46:02 +02:00
2017-05-10 16:20:38 +02:00
@@@ note
2013-09-14 14:19:18 +02:00
2017-05-10 16:20:38 +02:00
`persistenceId` must be unique to a given entity in the journal (database table/keyspace).
When replaying messages persisted to the journal, you query messages with a `persistenceId` .
So, if two different entities share the same `persistenceId` , message-replaying
behavior is corrupted.
2016-09-06 14:20:48 +09:00
2017-05-10 16:20:38 +02:00
@@@
2016-09-06 14:20:48 +09:00
2017-05-10 16:20:38 +02:00
### Recovery
2013-09-14 14:19:18 +02:00
2014-06-25 12:51:21 +02:00
By default, a persistent actor is automatically recovered on start and on restart by replaying journaled messages.
2015-10-13 22:27:14 +03:00
New messages sent to a persistent actor during recovery do not interfere with replayed messages.
2017-04-24 18:00:56 +02:00
They are stashed and received by a persistent actor after recovery phase completes.
2018-01-31 17:19:19 +01:00
The number of concurrent recoveries that can be in progress at the same time is limited
to not overload the system and the backend data store. When exceeding the limit the actors will wait
2017-05-10 16:20:38 +02:00
until other recoveries have been completed. This is configured by:
2017-04-24 18:00:56 +02:00
2017-05-10 16:20:38 +02:00
```
akka.persistence.max-concurrent-recoveries = 50
```
2013-09-14 14:19:18 +02:00
2017-05-10 16:20:38 +02:00
@@@ note
2015-07-01 17:00:11 +02:00
2017-07-18 00:08:34 -07:00
Accessing the @scala [`sender()` ]@java [sender with `getSender()` ] for replayed messages will always result in a `deadLetters` reference,
2017-05-10 16:20:38 +02:00
as the original sender is presumed to be long gone. If you indeed have to notify an actor during
recovery in the future, store its `ActorPath` explicitly in your persisted events.
2016-11-17 10:39:18 +01:00
2017-05-10 16:20:38 +02:00
@@@
2013-09-14 14:19:18 +02:00
2017-05-11 17:27:57 +02:00
< a id = "recovery-custom" > < / a >
2017-05-10 16:20:38 +02:00
#### Recovery customization
2016-11-17 10:39:18 +01:00
2017-05-10 16:20:38 +02:00
Applications may also customise how recovery is performed by returning a customised `Recovery` object
2017-06-26 18:15:43 +09:00
in the `recovery` method of a @scala [`PersistentActor` ]@java [`AbstractPersistentActor` ],
2017-05-10 16:20:38 +02:00
2017-06-26 18:15:43 +09:00
To skip loading snapshots and replay all events you can use @scala [`SnapshotSelectionCriteria.None` ]@java [`SnapshotSelectionCriteria.none()` ].
2016-11-17 10:39:18 +01:00
This can be useful if snapshot serialization format has changed in an incompatible way.
It should typically not be used when events have been deleted.
2017-06-26 18:15:43 +09:00
Scala
2018-09-13 10:49:51 +02:00
: @@snip [PersistenceDocSpec.scala ](/akka-docs/src/test/scala/docs/persistence/PersistenceDocSpec.scala ) { #recovery -no-snap }
2017-06-26 18:15:43 +09:00
Java
2018-09-13 10:49:51 +02:00
: @@snip [LambdaPersistenceDocTest.java ](/akka-docs/src/test/java/jdocs/persistence/LambdaPersistenceDocTest.java ) { #recovery -no-snap }
2016-11-17 10:39:18 +01:00
2017-07-19 03:49:51 -07:00
Another possible recovery customization, which can be useful for debugging, is setting an
upper bound on the replay, causing the actor to be replayed only up to a certain point "in the past" (instead of being replayed to its most up to date state). Note that after that it is a bad idea to persist new
2017-03-31 13:52:05 +03:00
events because a later recovery will probably be confused by the new events that follow the
2016-11-17 10:39:18 +01:00
events that were previously skipped.
2013-09-14 14:19:18 +02:00
2017-06-26 18:15:43 +09:00
Scala
2018-09-13 10:49:51 +02:00
: @@snip [PersistenceDocSpec.scala ](/akka-docs/src/test/scala/docs/persistence/PersistenceDocSpec.scala ) { #recovery -custom }
2017-06-26 18:15:43 +09:00
Java
2018-09-13 10:49:51 +02:00
: @@snip [LambdaPersistenceDocTest.java ](/akka-docs/src/test/java/jdocs/persistence/LambdaPersistenceDocTest.java ) { #recovery -custom }
2013-09-14 14:19:18 +02:00
2017-05-10 16:20:38 +02:00
Recovery can be disabled by returning `Recovery.none()` in the `recovery` method of a `PersistentActor` :
2013-09-14 14:19:18 +02:00
2017-06-26 18:15:43 +09:00
Scala
2018-09-13 10:49:51 +02:00
: @@snip [PersistenceDocSpec.scala ](/akka-docs/src/test/scala/docs/persistence/PersistenceDocSpec.scala ) { #recovery -disabled }
2017-06-26 18:15:43 +09:00
Java
2018-09-13 10:49:51 +02:00
: @@snip [LambdaPersistenceDocTest.java ](/akka-docs/src/test/java/jdocs/persistence/LambdaPersistenceDocTest.java ) { #recovery -disabled }
2013-09-14 14:19:18 +02:00
2017-05-10 16:20:38 +02:00
#### Recovery status
2013-09-14 14:19:18 +02:00
2014-06-25 12:51:21 +02:00
A persistent actor can query its own recovery status via the methods
2013-09-14 14:19:18 +02:00
2017-06-26 18:15:43 +09:00
Scala
2018-09-13 10:49:51 +02:00
: @@snip [PersistenceDocSpec.scala ](/akka-docs/src/test/scala/docs/persistence/PersistenceDocSpec.scala ) { #recovery -status }
2017-06-26 18:15:43 +09:00
Java
2018-09-13 10:49:51 +02:00
: @@snip [LambdaPersistenceDocTest.java ](/akka-docs/src/test/java/jdocs/persistence/LambdaPersistenceDocTest.java ) { #recovery -status }
2013-09-14 14:19:18 +02:00
2014-03-24 15:35:54 +01:00
Sometimes there is a need for performing additional initialization when the
2015-11-04 13:49:30 +01:00
recovery has completed before processing any other message sent to the persistent actor.
2017-05-10 16:20:38 +02:00
The persistent actor will receive a special `RecoveryCompleted` message right after recovery
2014-06-25 12:51:21 +02:00
and before any other received messages.
2017-06-26 18:15:43 +09:00
Scala
2018-09-13 10:49:51 +02:00
: @@snip [PersistenceDocSpec.scala ](/akka-docs/src/test/scala/docs/persistence/PersistenceDocSpec.scala ) { #recovery -completed }
2017-06-26 18:15:43 +09:00
Java
2018-09-13 10:49:51 +02:00
: @@snip [LambdaPersistenceDocTest.java ](/akka-docs/src/test/java/jdocs/persistence/LambdaPersistenceDocTest.java ) { #recovery -completed }
2014-12-14 21:45:22 +01:00
2017-05-10 16:20:38 +02:00
The actor will always receive a `RecoveryCompleted` message, even if there are no events
2016-11-04 15:26:14 +02:00
in the journal and the snapshot store is empty, or if it's a new persistent actor with a previously
2017-05-10 16:20:38 +02:00
unused `persistenceId` .
2016-11-04 15:26:14 +02:00
2017-05-10 16:20:38 +02:00
If there is a problem with recovering the state of the actor from the journal, `onRecoveryFailure`
2015-10-13 22:27:14 +03:00
is called (logging the error by default) and the actor will be stopped.
2014-03-24 15:35:54 +01:00
2017-05-10 16:20:38 +02:00
### Internal stash
2016-02-02 18:40:20 +08:00
2017-05-11 17:27:57 +02:00
The persistent actor has a private @ref: [stash ](actors.md#stash ) for internally caching incoming messages during
[recovery ](#recovery ) or the `persist\persistAll` method persisting events. You can still use/inherit from the
2017-05-10 16:20:38 +02:00
`Stash` interface. The internal stash cooperates with the normal stash by hooking into `unstashAll` method and
2016-02-19 15:14:20 +08:00
making sure messages are unstashed properly to the internal stash to maintain ordering guarantees.
2016-02-02 18:40:20 +08:00
2017-03-31 13:52:05 +03:00
You should be careful to not send more messages to a persistent actor than it can keep up with, otherwise the number
2017-05-10 16:20:38 +02:00
of stashed messages will grow without bounds. It can be wise to protect against `OutOfMemoryError` by defining a
maximum stash capacity in the mailbox configuration:
2016-02-02 18:40:20 +08:00
2017-05-10 16:20:38 +02:00
```
akka.actor.default-mailbox.stash-capacity=10000
```
2016-02-02 18:40:20 +08:00
Note that the stash capacity is per actor. If you have many persistent actors, e.g. when using cluster sharding,
you may need to define a small stash capacity to ensure that the total number of stashed messages in the system
2017-03-31 13:52:05 +03:00
doesn't consume too much memory. Additionally, the persistent actor defines three strategies to handle failure when the
2017-05-10 16:20:38 +02:00
internal stash capacity is exceeded. The default overflow strategy is the `ThrowOverflowExceptionStrategy` , which
discards the current received message and throws a `StashOverflowException` , causing actor restart if the default
supervision strategy is used. You can override the `internalStashOverflowStrategy` method to return
`DiscardToDeadLetterStrategy` or `ReplyToStrategy` for any "individual" persistent actor, or define the "default"
for all persistent actors by providing FQCN, which must be a subclass of `StashOverflowStrategyConfigurator` , in the
persistence configuration:
2016-02-02 18:40:20 +08:00
2017-05-10 16:20:38 +02:00
```
akka.persistence.internal-stash-overflow-strategy=
"akka.persistence.ThrowExceptionConfigurator"
```
2017-03-31 13:52:05 +03:00
2017-05-10 16:20:38 +02:00
The `DiscardToDeadLetterStrategy` strategy also has a pre-packaged companion configurator
`akka.persistence.DiscardConfigurator` .
2016-02-02 18:40:20 +08:00
2017-06-26 18:15:43 +09:00
You can also query the default strategy via the Akka persistence extension singleton:
Scala
: @@@vars
```
Persistence(context.system).defaultInternalStashOverflowStrategy
```
@@@
Java
: @@@vars
```
Persistence.get(getContext().getSystem()).defaultInternalStashOverflowStrategy();
```
@@@
2018-01-31 17:19:19 +01:00
2017-05-10 16:20:38 +02:00
@@@ note
2016-02-02 18:40:20 +08:00
2017-05-10 16:20:38 +02:00
The bounded mailbox should be avoided in the persistent actor, by which the messages come from storage backends may
be discarded. You can use bounded stash instead of it.
2014-06-25 12:51:21 +02:00
2017-05-10 16:20:38 +02:00
@@@
2014-06-25 12:51:21 +02:00
2017-05-11 17:27:57 +02:00
< a id = "persist-async" > < / a >
2017-05-10 16:20:38 +02:00
### Relaxed local consistency requirements and high throughput use-cases
If faced with relaxed local consistency requirements and high throughput demands sometimes `PersistentActor` and its
`persist` may not be enough in terms of consuming incoming Commands at a high rate, because it has to wait until all
2014-06-25 12:51:21 +02:00
Events related to a given Command are processed in order to start processing the next Command. While this abstraction is
very useful for most cases, sometimes you may be faced with relaxed requirements about consistency – for example you may
2015-11-04 13:49:30 +01:00
want to process commands as fast as you can, assuming that the Event will eventually be persisted and handled properly in
the background, retroactively reacting to persistence failures if needed.
2014-06-25 12:51:21 +02:00
2017-05-10 16:20:38 +02:00
The `persistAsync` method provides a tool for implementing high-throughput persistent actors. It will *not*
2014-06-25 12:51:21 +02:00
stash incoming Commands while the Journal is still working on persisting and/or user code is executing event callbacks.
In the below example, the event callbacks may be called "at any time", even after the next Command has been processed.
The ordering between events is still guaranteed ("evt-b-1" will be sent after "evt-a-2", which will be sent after "evt-a-1" etc.).
2017-06-26 18:15:43 +09:00
Scala
2018-09-13 10:49:51 +02:00
: @@snip [PersistenceDocSpec.scala ](/akka-docs/src/test/scala/docs/persistence/PersistenceDocSpec.scala ) { #persist -async }
2017-06-26 18:15:43 +09:00
Java
2018-09-13 10:49:51 +02:00
: @@snip [LambdaPersistenceDocTest.java ](/akka-docs/src/test/java/jdocs/persistence/LambdaPersistenceDocTest.java ) { #persist -async }
2017-05-10 16:20:38 +02:00
@@@ note
2018-05-15 08:11:03 +02:00
In order to implement the pattern known as "*command sourcing*" call @scala [`persistAsync(cmd)(...)` ]@java [`persistAsync` ] right away on all incoming
2017-05-10 16:20:38 +02:00
messages and handle them in the callback.
@@@
2014-06-25 12:51:21 +02:00
2017-05-10 16:20:38 +02:00
@@@ warning
2015-10-13 22:27:14 +03:00
2017-05-10 16:20:38 +02:00
The callback will not be invoked if the actor is restarted (or stopped) in between the call to
`persistAsync` and the journal has confirmed the write.
2014-06-25 12:51:21 +02:00
2017-05-10 16:20:38 +02:00
@@@
2014-06-25 12:51:21 +02:00
2017-05-11 17:27:57 +02:00
< a id = "defer" > < / a >
2017-05-10 16:20:38 +02:00
### Deferring actions until preceding persist handlers have executed
2014-06-25 12:51:21 +02:00
2017-05-10 16:20:38 +02:00
Sometimes when working with `persistAsync` or `persist` you may find that it would be nice to define some actions in terms of
2018-02-05 22:36:26 +01:00
''happens-after the previous `persistAsync` /`persist` handlers have been invoked''. `PersistentActor` provides utility methods
called `defer` and `deferAsync` , which work similarly to `persist` and `persistAsync` respectively yet do not persist the
passed in event. It is recommended to use them for *read* operations, and actions which do not have corresponding events in your
domain model.
2014-06-25 12:51:21 +02:00
2018-02-05 22:36:26 +01:00
Using those methods is very similar to the persist family of methods, yet they do **not** persist the passed in event.
2014-06-25 12:51:21 +02:00
It will be kept in memory and used when invoking the handler.
2014-03-24 15:35:54 +01:00
2017-06-26 18:15:43 +09:00
Scala
2018-09-13 10:49:51 +02:00
: @@snip [PersistenceDocSpec.scala ](/akka-docs/src/test/scala/docs/persistence/PersistenceDocSpec.scala ) { #defer }
2017-06-26 18:15:43 +09:00
Java
2018-09-13 10:49:51 +02:00
: @@snip [LambdaPersistenceDocTest.java ](/akka-docs/src/test/java/jdocs/persistence/LambdaPersistenceDocTest.java ) { #defer }
2013-09-14 14:19:18 +02:00
2017-05-10 16:20:38 +02:00
Notice that the `sender()` is **safe** to access in the handler callback, and will be pointing to the original sender
2018-02-05 22:36:26 +01:00
of the command for which this `defer` or `deferAsync` handler was called.
2013-09-14 14:19:18 +02:00
2014-06-25 12:51:21 +02:00
The calling side will get the responses in this (guaranteed) order:
2013-09-14 14:19:18 +02:00
2017-06-26 18:15:43 +09:00
Scala
2018-09-13 10:49:51 +02:00
: @@snip [PersistenceDocSpec.scala ](/akka-docs/src/test/scala/docs/persistence/PersistenceDocSpec.scala ) { #defer -caller }
2017-06-26 18:15:43 +09:00
Java
2018-09-13 10:49:51 +02:00
: @@snip [LambdaPersistenceDocTest.java ](/akka-docs/src/test/java/jdocs/persistence/LambdaPersistenceDocTest.java ) { #defer -caller }
2014-06-25 12:51:21 +02:00
2018-02-05 22:36:26 +01:00
You can also call `defer` or `deferAsync` with `persist` .
2017-03-30 18:34:09 +09:00
2017-06-26 18:15:43 +09:00
Scala
2018-09-13 10:49:51 +02:00
: @@snip [PersistenceDocSpec.scala ](/akka-docs/src/test/scala/docs/persistence/PersistenceDocSpec.scala ) { #defer -with-persist }
2017-06-26 18:15:43 +09:00
Java
2018-09-13 10:49:51 +02:00
: @@snip [LambdaPersistenceDocTest.java ](/akka-docs/src/test/java/jdocs/persistence/LambdaPersistenceDocTest.java ) { #defer -with-persist }
2017-03-30 18:34:09 +09:00
2017-05-10 16:20:38 +02:00
@@@ warning
2014-06-25 12:51:21 +02:00
2017-05-10 16:20:38 +02:00
The callback will not be invoked if the actor is restarted (or stopped) in between the call to
2018-02-05 22:36:26 +01:00
`defer` or `deferAsync` and the journal has processed and confirmed all preceding writes.
2015-06-24 19:58:43 +02:00
2017-05-10 16:20:38 +02:00
@@@
### Nested persist calls
It is possible to call `persist` and `persistAsync` inside their respective callback blocks and they will properly
2017-06-26 18:15:43 +09:00
retain both the thread safety (including the right value of @scala [`sender()` ]@java [`getSender()` ]) as well as stashing guarantees.
2015-06-24 19:58:43 +02:00
In general it is encouraged to create command handlers which do not need to resort to nested event persisting,
however there are situations where it may be useful. It is important to understand the ordering of callback execution in
2018-01-31 16:25:31 +01:00
those situations, as well as their implication on the stashing behavior (that `persist()` enforces). In the following
2015-06-24 19:58:43 +02:00
example two persist calls are issued, and each of them issues another persist inside its callback:
2017-06-26 18:15:43 +09:00
Scala
2018-09-13 10:49:51 +02:00
: @@snip [PersistenceDocSpec.scala ](/akka-docs/src/test/scala/docs/persistence/PersistenceDocSpec.scala ) { #nested -persist-persist }
2017-06-26 18:15:43 +09:00
Java
2018-09-13 10:49:51 +02:00
: @@snip [LambdaPersistenceDocTest.java ](/akka-docs/src/test/java/jdocs/persistence/LambdaPersistenceDocTest.java ) { #nested -persist-persist }
2015-06-24 19:58:43 +02:00
2017-05-10 16:20:38 +02:00
When sending two commands to this `PersistentActor` , the persist handlers will be executed in the following order:
2015-06-24 19:58:43 +02:00
2017-06-26 18:15:43 +09:00
Scala
2018-09-13 10:49:51 +02:00
: @@snip [PersistenceDocSpec.scala ](/akka-docs/src/test/scala/docs/persistence/PersistenceDocSpec.scala ) { #nested -persist-persist-caller }
2017-06-26 18:15:43 +09:00
Java
2018-09-13 10:49:51 +02:00
: @@snip [LambdaPersistenceDocTest.java ](/akka-docs/src/test/java/jdocs/persistence/LambdaPersistenceDocTest.java ) { #nested -persist-persist-caller }
2015-06-24 19:58:43 +02:00
2015-11-04 13:49:30 +01:00
First the "outer layer" of persist calls is issued and their callbacks are applied. After these have successfully completed,
2015-12-17 09:40:03 +01:00
the inner callbacks will be invoked (once the events they are persisting have been confirmed to be persisted by the journal).
2015-11-04 13:49:30 +01:00
Only after all these handlers have been successfully invoked will the next command be delivered to the persistent Actor.
2017-05-10 16:20:38 +02:00
In other words, the stashing of incoming commands that is guaranteed by initially calling `persist()` on the outer layer
is extended until all nested `persist` callbacks have been handled.
2015-06-24 19:58:43 +02:00
2017-05-10 16:20:38 +02:00
It is also possible to nest `persistAsync` calls, using the same pattern:
2015-06-24 19:58:43 +02:00
2017-06-26 18:15:43 +09:00
Scala
2018-09-13 10:49:51 +02:00
: @@snip [PersistenceDocSpec.scala ](/akka-docs/src/test/scala/docs/persistence/PersistenceDocSpec.scala ) { #nested -persistAsync-persistAsync }
2017-06-26 18:15:43 +09:00
Java
2018-09-13 10:49:51 +02:00
: @@snip [LambdaPersistenceDocTest.java ](/akka-docs/src/test/java/jdocs/persistence/LambdaPersistenceDocTest.java ) { #nested -persistAsync-persistAsync }
2015-06-24 19:58:43 +02:00
2015-11-04 13:49:30 +01:00
In this case no stashing is happening, yet events are still persisted and callbacks are executed in the expected order:
2015-06-24 19:58:43 +02:00
2017-06-26 18:15:43 +09:00
Scala
2018-09-13 10:49:51 +02:00
: @@snip [PersistenceDocSpec.scala ](/akka-docs/src/test/scala/docs/persistence/PersistenceDocSpec.scala ) { #nested -persistAsync-persistAsync-caller }
2017-06-26 18:15:43 +09:00
Java
2018-09-13 10:49:51 +02:00
: @@snip [LambdaPersistenceDocTest.java ](/akka-docs/src/test/java/jdocs/persistence/LambdaPersistenceDocTest.java ) { #nested -persistAsync-persistAsync-caller }
2015-06-24 19:58:43 +02:00
2017-05-10 16:20:38 +02:00
While it is possible to nest mixed `persist` and `persistAsync` with keeping their respective semantics
2015-11-04 13:49:30 +01:00
it is not a recommended practice, as it may lead to overly complex nesting.
2015-06-24 19:58:43 +02:00
2017-05-10 16:20:38 +02:00
@@@ warning
2016-10-21 16:36:11 +02:00
2017-05-10 16:20:38 +02:00
While it is possible to nest `persist` calls within one another,
it is *not* legal call `persist` from any other Thread than the Actors message processing Thread.
For example, it is not legal to call `persist` from Futures! Doing so will break the guarantees
that the persist methods aim to provide. Always call `persist` and `persistAsync` from within
the Actor's receive block (or methods synchronously invoked from there).
2015-07-02 00:44:10 +02:00
2017-05-10 16:20:38 +02:00
@@@
2015-06-23 21:01:36 +02:00
2017-05-10 16:20:38 +02:00
### Failures
If persistence of an event fails, `onPersistFailure` will be invoked (logging the error by default),
2015-10-13 22:27:14 +03:00
and the actor will unconditionally be stopped.
2015-06-23 21:01:36 +02:00
2015-11-04 13:49:30 +01:00
The reason that it cannot resume when persist fails is that it is unknown if the event was actually
2015-10-13 22:27:14 +03:00
persisted or not, and therefore it is in an inconsistent state. Restarting on persistent failures
2015-11-04 13:49:30 +01:00
will most likely fail anyway since the journal is probably unavailable. It is better to stop the
2017-05-10 16:20:38 +02:00
actor and after a back-off timeout start it again. The `akka.pattern.BackoffSupervisor` actor
2015-06-23 21:01:36 +02:00
is provided to support such restarts.
2017-06-26 18:15:43 +09:00
Scala
2018-09-13 10:49:51 +02:00
: @@snip [PersistenceDocSpec.scala ](/akka-docs/src/test/scala/docs/persistence/PersistenceDocSpec.scala ) { #backoff }
2017-06-26 18:15:43 +09:00
Java
2018-09-13 10:49:51 +02:00
: @@snip [LambdaPersistenceDocTest.java ](/akka-docs/src/test/java/jdocs/persistence/LambdaPersistenceDocTest.java ) { #backoff }
2015-06-23 21:01:36 +02:00
2019-09-27 09:50:34 +02:00
See @ref: [Backoff Supervision strategies ](fault-tolerance.md#supervision-strategies ) for more details about actor supervision.
2019-03-07 09:58:31 +01:00
2015-10-13 22:27:14 +03:00
If persistence of an event is rejected before it is stored, e.g. due to serialization error,
2017-05-10 16:20:38 +02:00
`onPersistRejected` will be invoked (logging a warning by default), and the actor continues with
2015-06-23 21:01:36 +02:00
next message.
If there is a problem with recovering the state of the actor from the journal when the actor is
2017-05-10 16:20:38 +02:00
started, `onRecoveryFailure` is called (logging the error by default), and the actor will be stopped.
2016-11-17 10:39:18 +01:00
Note that failure to load snapshot is also treated like this, but you can disable loading of snapshots
2019-10-01 15:29:12 +02:00
if you for example know that serialization format has changed in an incompatible way, see @ref: [Recovery customization ](#recovery-custom ).
2015-06-26 10:36:27 +02:00
2017-05-10 16:20:38 +02:00
### Atomic writes
2015-06-23 21:01:36 +02:00
2018-05-15 08:11:03 +02:00
Each event is stored atomically, but it is also possible to store several events atomically by
2017-05-10 16:20:38 +02:00
using the `persistAll` or `persistAllAsync` method. That means that all events passed to that method
2015-10-13 22:27:14 +03:00
are stored or none of them are stored if there is an error.
2015-06-23 21:01:36 +02:00
The recovery of a persistent actor will therefore never be done partially with only a subset of events persisted by
2017-05-10 16:20:38 +02:00
*persistAll*.
2015-06-23 21:01:36 +02:00
2017-05-10 16:20:38 +02:00
Some journals may not support atomic writes of several events and they will then reject the `persistAll`
command, i.e. `onPersistRejected` is called with an exception (typically `UnsupportedOperationException` ).
2014-06-25 12:51:21 +02:00
2017-05-10 16:20:38 +02:00
### Batch writes
2014-06-25 12:51:21 +02:00
2017-05-10 16:20:38 +02:00
In order to optimize throughput when using `persistAsync` , a persistent actor
2016-02-05 18:19:24 +01:00
internally batches events to be stored under high load before writing them to
the journal (as a single batch). The batch size is dynamically determined by
how many events are emitted during the time of a journal round-trip: after
sending a batch to the journal no further batch can be sent before confirmation
has been received that the previous batch has been written. Batch writes are never
timer-based which keeps latencies at a minimum.
2014-06-25 12:51:21 +02:00
2017-05-10 16:20:38 +02:00
### Message deletion
2013-11-07 10:45:02 +01:00
2015-11-04 13:49:30 +01:00
It is possible to delete all messages (journaled by a single persistent actor) up to a specified sequence number;
2017-05-10 16:20:38 +02:00
Persistent actors may call the `deleteMessages` method to this end.
2014-06-27 08:10:20 +02:00
2020-12-07 08:41:43 +01:00
Deleting messages in Event Sourcing based applications is typically either not used at all, or used in conjunction with
2017-05-10 16:20:38 +02:00
[snapshotting ](#snapshots ), i.e. after a snapshot has been successfully stored, a `deleteMessages(toSequenceNr)`
2015-11-04 13:49:30 +01:00
up until the sequence number of the data held by that snapshot can be issued to safely delete the previous events
2015-07-02 00:44:10 +02:00
while still having access to the accumulated state during replays - by loading the snapshot.
2017-05-10 16:20:38 +02:00
@@@ warning
2016-10-09 06:07:06 +09:00
2017-05-10 16:20:38 +02:00
If you are using @ref: [Persistence Query ](persistence-query.md ), query results may be missing deleted messages in a journal,
depending on how deletions are implemented in the journal plugin.
Unless you use a plugin which still shows deleted messages in persistence query results,
you have to design your application so that it is not affected by missing messages.
2015-08-13 15:13:27 +02:00
2017-05-10 16:20:38 +02:00
@@@
The result of the `deleteMessages` request is signaled to the persistent actor with a `DeleteMessagesSuccess`
message if the delete was successful or a `DeleteMessagesFailure` message if it failed.
Message deletion doesn't affect the highest sequence number of the journal, even if all messages were deleted from it after `deleteMessages` invocation.
### Persistence status handling
2015-10-13 22:27:14 +03:00
2015-11-04 13:49:30 +01:00
Persisting, deleting, and replaying messages can either succeed or fail.
2015-07-02 00:44:10 +02:00
2017-05-10 16:20:38 +02:00
|**Method** | **Success** |
2017-05-24 23:57:09 +02:00
|---------------------------|------------------------|
2017-05-10 16:20:38 +02:00
|`persist` / `persistAsync` | persist handler invoked|
|`onPersistRejected` | No automatic actions. |
|`recovery` | `RecoveryCompleted` |
|`deleteMessages` | `DeleteMessagesSuccess` |
The most important operations (`persist` and `recovery` ) have failure handlers modelled as explicit callbacks which
the user can override in the `PersistentActor` . The default implementations of these handlers emit a log message
(`error` for persist/recovery failures, and `warning` for others), logging the failure cause and information about
2015-07-02 00:44:10 +02:00
which message caused the failure.
For critical failures, such as recovery or persisting events failing, the persistent actor will be stopped after the failure
handler is invoked. This is because if the underlying journal implementation is signalling persistence failures it is most
likely either failing completely or overloaded and restarting right-away and trying to persist the event again will most
2017-05-10 16:20:38 +02:00
likely not help the journal recover – as it would likely cause a [Thundering herd problem ](https://en.wikipedia.org/wiki/Thundering_herd_problem ), as many persistent actors
2019-10-01 15:29:12 +02:00
would restart and try to persist their events again. Instead, using a `BackoffSupervisor` (as described in @ref: [Failures ](#failures )) which
2015-07-02 00:44:10 +02:00
implements an exponential-backoff strategy which allows for more breathing room for the journal to recover between
restarts of the persistent actor.
2017-05-10 16:20:38 +02:00
@@@ note
2015-07-02 00:44:10 +02:00
2017-05-10 16:20:38 +02:00
Journal implementations may choose to implement a retry mechanism, e.g. such that only after a write fails N number
of times a persistence failure is signalled back to the user. In other words, once a journal returns a failure,
it is considered *fatal* by Akka Persistence, and the persistent actor which caused the failure will be stopped.
2015-07-02 00:44:10 +02:00
2017-05-10 16:20:38 +02:00
Check the documentation of the journal implementation you are using for details if/how it is using this technique.
2015-07-02 00:44:10 +02:00
2017-05-10 16:20:38 +02:00
@@@
2015-08-19 18:20:13 +02:00
2017-05-11 17:27:57 +02:00
< a id = "safe-shutdown" > < / a >
2017-05-10 16:20:38 +02:00
### Safely shutting down persistent actors
2015-08-19 18:20:13 +02:00
2015-11-04 13:49:30 +01:00
Special care should be given when shutting down persistent actors from the outside.
2017-05-11 17:27:57 +02:00
With normal Actors it is often acceptable to use the special @ref: [PoisonPill ](actors.md#poison-pill ) message
2015-08-19 18:20:13 +02:00
to signal to an Actor that it should stop itself once it receives this message – in fact this message is handled
automatically by Akka, leaving the target actor no way to refuse stopping itself when given a poison pill.
2017-05-10 16:20:38 +02:00
This can be dangerous when used with `PersistentActor` due to the fact that incoming commands are *stashed* while
the persistent actor is awaiting confirmation from the Journal that events have been written when `persist()` was used.
2015-11-04 13:49:30 +01:00
Since the incoming commands will be drained from the Actor's mailbox and put into its internal stash while awaiting the
2015-08-19 18:20:13 +02:00
confirmation (thus, before calling the persist handlers) the Actor **may receive and (auto)handle the PoisonPill
before it processes the other messages which have been put into its stash**, causing a pre-mature shutdown of the Actor.
2017-05-10 16:20:38 +02:00
@@@ warning
2015-08-19 18:20:13 +02:00
2017-05-10 16:20:38 +02:00
Consider using explicit shut-down messages instead of `PoisonPill` when working with persistent actors.
2015-06-26 10:36:27 +02:00
2017-05-10 16:20:38 +02:00
@@@
2016-09-12 23:18:06 +09:00
2017-05-10 16:20:38 +02:00
The example below highlights how messages arrive in the Actor's mailbox and how they interact with its internal stashing
2018-01-31 16:25:31 +01:00
mechanism when `persist()` is used. Notice the early stop behavior that occurs when `PoisonPill` is used:
2016-09-12 23:18:06 +09:00
2017-06-26 18:15:43 +09:00
Scala
2018-09-13 10:49:51 +02:00
: @@snip [PersistenceDocSpec.scala ](/akka-docs/src/test/scala/docs/persistence/PersistenceDocSpec.scala ) { #safe -shutdown }
2017-06-26 18:15:43 +09:00
Java
2018-09-13 10:49:51 +02:00
: @@snip [LambdaPersistenceDocTest.java ](/akka-docs/src/test/java/jdocs/persistence/LambdaPersistenceDocTest.java ) { #safe -shutdown }
2017-06-26 18:15:43 +09:00
Scala
2018-09-13 10:49:51 +02:00
: @@snip [PersistenceDocSpec.scala ](/akka-docs/src/test/scala/docs/persistence/PersistenceDocSpec.scala ) { #safe -shutdown-example-bad }
2017-06-26 18:15:43 +09:00
Java
2018-09-13 10:49:51 +02:00
: @@snip [LambdaPersistenceDocTest.java ](/akka-docs/src/test/java/jdocs/persistence/LambdaPersistenceDocTest.java ) { #safe -shutdown-example-bad }
2017-06-26 18:15:43 +09:00
2016-09-12 23:18:06 +09:00
2017-06-26 18:15:43 +09:00
Scala
2018-09-13 10:49:51 +02:00
: @@snip [PersistenceDocSpec.scala ](/akka-docs/src/test/scala/docs/persistence/PersistenceDocSpec.scala ) { #safe -shutdown-example-good }
2016-09-12 23:18:06 +09:00
2017-06-26 18:15:43 +09:00
Java
2018-09-13 10:49:51 +02:00
: @@snip [LambdaPersistenceDocTest.java ](/akka-docs/src/test/java/jdocs/persistence/LambdaPersistenceDocTest.java ) { #safe -shutdown-example-good }
2016-09-12 23:18:06 +09:00
2017-05-10 16:20:38 +02:00
### Replay Filter
2016-09-12 23:18:06 +09:00
2019-09-26 22:48:12 +02:00
See @ref: [Replay filter ](typed/persistence.md#replay-filter ) in the documentation of the new API.
2017-05-10 16:20:38 +02:00
## Snapshots
2013-09-26 09:14:43 +02:00
2018-01-31 17:19:19 +01:00
As you model your domain using actors, you may notice that some actors may be prone to accumulating extremely long event logs and experiencing long recovery times. Sometimes, the right approach may be to split out into a set of shorter lived actors. However, when this is not an option, you can use snapshots to reduce recovery times drastically.
2014-01-17 06:58:25 +01:00
2017-05-10 16:20:38 +02:00
Persistent actors can save snapshots of internal state by calling the `saveSnapshot` method. If saving of a snapshot
succeeds, the persistent actor receives a `SaveSnapshotSuccess` message, otherwise a `SaveSnapshotFailure` message
2013-09-26 09:14:43 +02:00
2017-06-26 18:15:43 +09:00
Scala
2018-09-13 10:49:51 +02:00
: @@snip [PersistenceDocSpec.scala ](/akka-docs/src/test/scala/docs/persistence/PersistenceDocSpec.scala ) { #save -snapshot }
2017-06-26 18:15:43 +09:00
Java
2018-09-13 10:49:51 +02:00
: @@snip [LambdaPersistenceDocTest.java ](/akka-docs/src/test/java/jdocs/persistence/LambdaPersistenceDocTest.java ) { #save -snapshot }
2013-09-26 09:14:43 +02:00
2020-07-16 16:06:04 +01:00
where `metadata` is of type `SnapshotMetadata` and contains:
2013-09-26 09:14:43 +02:00
2020-07-16 16:06:04 +01:00
* persistenceId
* sequenceNr
* timestamp
2013-09-26 09:14:43 +02:00
2019-02-22 09:00:56 +01:00
During recovery, the persistent actor is offered the latest saved snapshot via a `SnapshotOffer` message from
2013-09-26 09:14:43 +02:00
which it can initialize internal state.
2017-06-26 18:15:43 +09:00
Scala
2018-09-13 10:49:51 +02:00
: @@snip [PersistenceDocSpec.scala ](/akka-docs/src/test/scala/docs/persistence/PersistenceDocSpec.scala ) { #snapshot -offer }
2017-06-26 18:15:43 +09:00
Java
2018-09-13 10:49:51 +02:00
: @@snip [LambdaPersistenceDocTest.java ](/akka-docs/src/test/java/jdocs/persistence/LambdaPersistenceDocTest.java ) { #snapshot -offer }
2013-09-26 09:14:43 +02:00
2017-05-10 16:20:38 +02:00
The replayed messages that follow the `SnapshotOffer` message, if any, are younger than the offered snapshot.
2014-06-25 12:51:21 +02:00
They finally recover the persistent actor to its current (i.e. latest) state.
2013-09-26 09:14:43 +02:00
2014-06-25 12:51:21 +02:00
In general, a persistent actor is only offered a snapshot if that persistent actor has previously saved one or more snapshots
2017-05-10 16:20:38 +02:00
and at least one of these snapshots matches the `SnapshotSelectionCriteria` that can be specified for recovery.
2017-06-26 18:15:43 +09:00
Scala
2018-09-13 10:49:51 +02:00
: @@snip [PersistenceDocSpec.scala ](/akka-docs/src/test/scala/docs/persistence/PersistenceDocSpec.scala ) { #snapshot -criteria }
2013-09-26 09:14:43 +02:00
2017-06-26 18:15:43 +09:00
Java
2018-09-13 10:49:51 +02:00
: @@snip [LambdaPersistenceDocTest.java ](/akka-docs/src/test/java/jdocs/persistence/LambdaPersistenceDocTest.java ) { #snapshot -criteria }
2017-06-26 18:15:43 +09:00
If not specified, they default to @scala [`SnapshotSelectionCriteria.Latest` ]@java [`SnapshotSelectionCriteria.latest()` ] which selects the latest (= youngest) snapshot.
To disable snapshot-based recovery, applications should use @scala [`SnapshotSelectionCriteria.None` ]@java [`SnapshotSelectionCriteria.none()` ]. A recovery where no
2017-05-10 16:20:38 +02:00
saved snapshot matches the specified `SnapshotSelectionCriteria` will replay all journaled messages.
2013-09-26 09:14:43 +02:00
2017-05-10 16:20:38 +02:00
@@@ note
2013-09-26 09:14:43 +02:00
2017-05-10 16:20:38 +02:00
In order to use snapshots, a default snapshot-store (`akka.persistence.snapshot-store.plugin` ) must be configured,
2017-07-26 00:06:32 -07:00
or the @scala [`PersistentActor` ]@java [persistent actor] can pick a snapshot store explicitly by overriding @scala [`def snapshotPluginId: String` ]@java [`String snapshotPluginId()` ].
2015-07-22 16:22:55 +02:00
2019-02-22 09:00:56 +01:00
Because some use cases may not benefit from or need snapshots, it is perfectly valid not to not configure a snapshot store.
2017-05-10 16:20:38 +02:00
However, Akka will log a warning message when this situation is detected and then continue to operate until
an actor tries to store a snapshot, at which point the operation will fail (by replying with an `SaveSnapshotFailure` for example).
2015-10-13 22:27:14 +03:00
2017-07-27 01:20:47 -07:00
Note that the "persistence mode" of @ref: [Cluster Sharding ](cluster-sharding.md ) makes use of snapshots. If you use that mode, you'll need to define a snapshot store plugin.
2015-07-22 16:22:55 +02:00
2017-05-10 16:20:38 +02:00
@@@
2013-11-12 09:02:02 +01:00
2017-05-10 16:20:38 +02:00
### Snapshot deletion
A persistent actor can delete individual snapshots by calling the `deleteSnapshot` method with the sequence number of
2015-06-17 01:23:18 +02:00
when the snapshot was taken.
2017-05-10 16:20:38 +02:00
To bulk-delete a range of snapshots matching `SnapshotSelectionCriteria` ,
2018-09-25 13:38:18 +01:00
persistent actors should use the `deleteSnapshots` method. Depending on the journal used this might be inefficient. It is
best practice to do specific deletes with `deleteSnapshot` or to include a `minSequenceNr` as well as a `maxSequenceNr`
for the `SnapshotSelectionCriteria` .
2013-11-12 09:02:02 +01:00
2017-05-10 16:20:38 +02:00
### Snapshot status handling
2015-07-02 00:44:10 +02:00
Saving or deleting snapshots can either succeed or fail – this information is reported back to the persistent actor via
status messages as illustrated in the following table.
2017-05-10 16:20:38 +02:00
|**Method** | **Success** | **Failure message** |
|---------------------------------------------|--------------------------|-------------------------|
|`saveSnapshot(Any)` | `SaveSnapshotSuccess` | `SaveSnapshotFailure` |
|`deleteSnapshot(Long)` | `DeleteSnapshotSuccess` | `DeleteSnapshotFailure` |
|`deleteSnapshots(SnapshotSelectionCriteria)` | `DeleteSnapshotsSuccess` | `DeleteSnapshotsFailure` |
2015-07-02 00:44:10 +02:00
If failure messages are left unhandled by the actor, a default warning log message will be logged for each incoming failure message.
No default action is performed on the success messages, however you're free to handle them e.g. in order to delete
2015-07-07 16:55:35 +02:00
an in memory representation of the snapshot, or in the case of failure to attempt save the snapshot again.
2015-07-02 00:44:10 +02:00
2021-05-31 21:50:28 +02:00
### Optional snapshots
By default, the persistent actor will unconditionally be stopped if the snapshot can't be loaded in the recovery.
It is possible to make snapshot loading optional. This can be useful when it is alright to ignore snapshot in case
of for example deserialization errors. When snapshot loading fails it will instead recover by replaying all events.
Enable this feature by setting `snapshot-is-optional = true` in the snapshot store configuration.
@@@ warning
Don't set `snapshot-is-optional = true` if events have been deleted because that would result in wrong recovered state if snapshot load fails.
@@@
2018-04-11 13:34:13 +02:00
## Scaling out
2019-09-26 22:48:12 +02:00
See @ref: [Scaling out ](typed/persistence.md#scaling-out ) in the documentation of the new API.
2018-04-11 13:34:13 +02:00
2017-05-10 16:20:38 +02:00
## At-Least-Once Delivery
2014-06-03 15:10:56 +02:00
2017-06-26 18:15:43 +09:00
To send messages with at-least-once delivery semantics to destinations you can @scala [mix-in `AtLeastOnceDelivery` trait to your `PersistentActor` ]@java [extend the `AbstractPersistentActorWithAtLeastOnceDelivery` class instead of `AbstractPersistentActor` ]
on the sending side. It takes care of re-sending messages when they
2014-06-03 15:10:56 +02:00
have not been confirmed within a configurable timeout.
2015-11-04 13:49:30 +01:00
The state of the sending actor, including which messages have been sent that have not been
2016-03-28 14:41:57 +02:00
confirmed by the recipient must be persistent so that it can survive a crash of the sending actor
2017-06-26 18:15:43 +09:00
or JVM. The @scala [`AtLeastOnceDelivery` trait]@java [`AbstractPersistentActorWithAtLeastOnceDelivery` class] does not persist anything by itself. It is your
2015-09-08 15:02:21 +02:00
responsibility to persist the intent that a message is sent and that a confirmation has been
received.
2017-05-10 16:20:38 +02:00
@@@ note
2014-06-03 15:10:56 +02:00
2017-05-10 16:20:38 +02:00
At-least-once delivery implies that original message sending order is not always preserved,
and the destination may receive duplicate messages.
Semantics do not match those of a normal `ActorRef` send operation:
2014-06-03 15:10:56 +02:00
2017-05-10 16:20:38 +02:00
* it is not at-most-once delivery
* message order for the same sender– receiver pair is not preserved due to
possible resends
* after a crash and restart of the destination messages are still
delivered to the new actor incarnation
2014-06-03 15:10:56 +02:00
2017-05-10 16:20:38 +02:00
These semantics are similar to what an `ActorPath` represents (see
2017-05-11 17:27:57 +02:00
@ref: [Actor Lifecycle ](actors.md#actor-lifecycle )), therefore you need to supply a path and not a
2017-05-10 16:20:38 +02:00
reference when delivering messages. The messages are sent to the path with
an actor selection.
2014-06-03 15:10:56 +02:00
2017-05-10 16:20:38 +02:00
@@@
2014-06-03 15:10:56 +02:00
2017-05-10 16:20:38 +02:00
Use the `deliver` method to send a message to a destination. Call the `confirmDelivery` method
2014-06-03 15:10:56 +02:00
when the destination has replied with a confirmation message.
2017-05-10 16:20:38 +02:00
### Relationship between deliver and confirmDelivery
2014-08-31 19:48:09 +01:00
2017-05-10 16:20:38 +02:00
To send messages to the destination path, use the `deliver` method after you have persisted the intent
2015-10-13 22:27:14 +03:00
to send the message.
2015-09-08 15:02:21 +02:00
The destination actor must send back a confirmation message. When the sending actor receives this
confirmation message you should persist the fact that the message was delivered successfully and then call
2017-05-10 16:20:38 +02:00
the `confirmDelivery` method.
2015-09-08 15:02:21 +02:00
2017-05-10 16:20:38 +02:00
If the persistent actor is not currently recovering, the `deliver` method will send the message to
the destination actor. When recovering, messages will be buffered until they have been confirmed using `confirmDelivery` .
2015-10-13 22:27:14 +03:00
Once recovery has completed, if there are outstanding messages that have not been confirmed (during the message replay),
2014-08-31 19:48:09 +01:00
the persistent actor will resend these before sending any other messages.
2017-05-10 16:20:38 +02:00
Deliver requires a `deliveryIdToMessage` function to pass the provided `deliveryId` into the message so that the correlation
between `deliver` and `confirmDelivery` is possible. The `deliveryId` must do the round trip. Upon receipt
2015-11-04 13:49:30 +01:00
of the message, the destination actor will send the same``deliveryId` ` wrapped in a confirmation message back to the sender.
2017-05-10 16:20:38 +02:00
The sender will then use it to call `confirmDelivery` method to complete the delivery routine.
2014-08-31 19:48:09 +01:00
2017-06-26 18:15:43 +09:00
Scala
2018-09-13 10:49:51 +02:00
: @@snip [PersistenceDocSpec.scala ](/akka-docs/src/test/scala/docs/persistence/PersistenceDocSpec.scala ) { #at -least-once-example }
2017-06-26 18:15:43 +09:00
Java
2018-09-13 10:49:51 +02:00
: @@snip [LambdaPersistenceDocTest.java ](/akka-docs/src/test/java/jdocs/persistence/LambdaPersistenceDocTest.java ) { #at -least-once-example }
2014-06-03 15:10:56 +02:00
2017-05-10 16:20:38 +02:00
The `deliveryId` generated by the persistence module is a strictly monotonically increasing sequence number
2015-10-13 22:27:14 +03:00
without gaps. The same sequence is used for all destinations of the actor, i.e. when sending to multiple
2017-05-10 16:20:38 +02:00
destinations the destinations will see gaps in the sequence. It is not possible to use custom `deliveryId` .
2015-10-13 22:27:14 +03:00
However, you can send a custom correlation identifier in the message to the destination. You must then retain
2017-05-10 16:20:38 +02:00
a mapping between the internal `deliveryId` (passed into the `deliveryIdToMessage` function) and your custom
correlation id (passed into the message). You can do this by storing such mapping in a `Map(correlationId -> deliveryId)`
from which you can retrieve the `deliveryId` to be passed into the `confirmDelivery` method once the receiver
2015-02-20 09:07:43 +01:00
of your message has replied with your custom correlation id.
2014-06-03 15:10:56 +02:00
2017-06-26 18:15:43 +09:00
The @scala [`AtLeastOnceDelivery` trait]@java [`AbstractPersistentActorWithAtLeastOnceDelivery` class] has a state consisting of unconfirmed messages and a
2014-06-03 15:10:56 +02:00
sequence number. It does not store this state itself. You must persist events corresponding to the
2017-05-10 16:20:38 +02:00
`deliver` and `confirmDelivery` invocations from your `PersistentActor` so that the state can
be restored by calling the same methods during the recovery phase of the `PersistentActor` . Sometimes
2014-06-03 15:10:56 +02:00
these events can be derived from other business level events, and sometimes you must create separate events.
2017-05-10 16:20:38 +02:00
During recovery, calls to `deliver` will not send out messages, those will be sent later
if no matching `confirmDelivery` will have been performed.
2014-06-03 15:10:56 +02:00
2017-05-10 16:20:38 +02:00
Support for snapshots is provided by `getDeliverySnapshot` and `setDeliverySnapshot` .
The `AtLeastOnceDeliverySnapshot` contains the full delivery state, including unconfirmed messages.
2014-06-03 15:10:56 +02:00
If you need a custom snapshot for other parts of the actor state you must also include the
2017-05-10 16:20:38 +02:00
`AtLeastOnceDeliverySnapshot` . It is serialized using protobuf with the ordinary Akka
serialization mechanism. It is easiest to include the bytes of the `AtLeastOnceDeliverySnapshot`
2014-06-03 15:10:56 +02:00
as a blob in your custom snapshot.
2017-05-10 16:20:38 +02:00
The interval between redelivery attempts is defined by the `redeliverInterval` method.
The default value can be configured with the `akka.persistence.at-least-once-delivery.redeliver-interval`
2014-06-03 15:10:56 +02:00
configuration key. The method can be overridden by implementation classes to return non-default values.
2014-11-25 11:34:43 +01:00
The maximum number of messages that will be sent at each redelivery burst is defined by the
2017-05-10 16:20:38 +02:00
`redeliveryBurstLimit` method (burst frequency is half of the redelivery interval). If there's a lot of
2014-11-25 11:34:43 +01:00
unconfirmed messages (e.g. if the destination is not available for a long time), this helps to prevent an overwhelming
amount of messages to be sent at once. The default value can be configured with the
2017-05-10 16:20:38 +02:00
`akka.persistence.at-least-once-delivery.redelivery-burst-limit` configuration key. The method can be overridden
2014-11-25 11:34:43 +01:00
by implementation classes to return non-default values.
2017-05-10 16:20:38 +02:00
After a number of delivery attempts a `AtLeastOnceDelivery.UnconfirmedWarning` message
will be sent to `self` . The re-sending will still continue, but you can choose to call
`confirmDelivery` to cancel the re-sending. The number of delivery attempts before emitting the
warning is defined by the `warnAfterNumberOfUnconfirmedAttempts` method. The default value can be
configured with the `akka.persistence.at-least-once-delivery.warn-after-number-of-unconfirmed-attempts`
2014-06-03 15:10:56 +02:00
configuration key. The method can be overridden by implementation classes to return non-default values.
2017-06-26 18:15:43 +09:00
The @scala [`AtLeastOnceDelivery` trait]@java [`AbstractPersistentActorWithAtLeastOnceDelivery` class] holds messages in memory until their successful delivery has been confirmed.
2015-11-04 13:49:30 +01:00
The maximum number of unconfirmed messages that the actor is allowed to hold in memory
2017-05-10 16:20:38 +02:00
is defined by the `maxUnconfirmedMessages` method. If this limit is exceed the `deliver` method will
not accept more messages and it will throw `AtLeastOnceDelivery.MaxUnconfirmedMessagesExceededException` .
The default value can be configured with the `akka.persistence.at-least-once-delivery.max-unconfirmed-messages`
2014-06-03 15:10:56 +02:00
configuration key. The method can be overridden by implementation classes to return non-default values.
2013-10-27 08:01:14 +01:00
2017-05-10 16:20:38 +02:00
## Event Adapters
2015-05-29 18:20:51 +02:00
2020-12-07 08:41:43 +01:00
In long running projects using Event Sourcing sometimes the need arises to detach the data model from the domain model
2015-05-29 18:20:51 +02:00
completely.
Event Adapters help in situations where:
2017-05-10 16:20:38 +02:00
* **Version Migrations** – existing events stored in *Version 1* should be "upcasted" to a new *Version 2* representation,
and the process of doing so involves actual code, not just changes on the serialization layer. For these scenarios
the `toJournal` function is usually an identity function, however the `fromJournal` is implemented as
2017-12-04 11:24:21 +01:00
`v1.Event=>v2.Event` , performing the necessary mapping inside the fromJournal method.
This technique is sometimes referred to as "upcasting" in other CQRS libraries.
2017-05-10 16:20:38 +02:00
* **Separating Domain and Data models** – thanks to EventAdapters it is possible to completely separate the domain model
from the model used to persist data in the Journals. For example one may want to use case classes in the
domain model, however persist their protocol-buffer (or any other binary serialization format) counter-parts to the Journal.
A simple `toJournal:MyModel=>MyDataModel` and `fromJournal:MyDataModel=>MyModel` adapter can be used to implement this feature.
* **Journal Specialized Data Types** – exposing data types understood by the underlying Journal, for example for data stores which
understand JSON it is possible to write an EventAdapter `toJournal:Any=>JSON` such that the Journal can *directly* store the
json instead of serializing the object to its binary representation.
2015-05-29 18:20:51 +02:00
2019-12-19 01:57:55 -05:00
Implementing an EventAdapter is rather straightforward:
2015-05-29 18:20:51 +02:00
2017-06-26 18:15:43 +09:00
Scala
2018-09-13 10:49:51 +02:00
: @@snip [PersistenceEventAdapterDocSpec.scala ](/akka-docs/src/test/scala/docs/persistence/PersistenceEventAdapterDocSpec.scala ) { #identity -event-adapter }
2017-06-26 18:15:43 +09:00
Java
2018-09-13 10:49:51 +02:00
: @@snip [PersistenceEventAdapterDocTest.java ](/akka-docs/src/test/java/jdocs/persistence/PersistenceEventAdapterDocTest.java ) { #identity -event-adapter }
2015-05-29 18:20:51 +02:00
Then in order for it to be used on events coming to and from the journal you must bind it using the below configuration syntax:
2018-09-13 10:49:51 +02:00
@@snip [PersistenceEventAdapterDocSpec.scala ](/akka-docs/src/test/scala/docs/persistence/PersistenceEventAdapterDocSpec.scala ) { #event -adapters-config }
2015-05-29 18:20:51 +02:00
2017-05-10 16:20:38 +02:00
It is possible to bind multiple adapters to one class *for recovery* , in which case the `fromJournal` methods of all
2015-05-29 18:20:51 +02:00
bound adapters will be applied to a given matching event (in order of definition in the configuration). Since each adapter may
2017-05-10 16:20:38 +02:00
return from `0` to `n` adapted events (called as `EventSeq` ), each adapter can investigate the event and if it should
2015-11-04 13:49:30 +01:00
indeed adapt it return the adapted event(s) for it. Other adapters which do not have anything to contribute during this
2017-05-10 16:20:38 +02:00
adaptation simply return `EventSeq.empty` . The adapted events are then delivered in-order to the `PersistentActor` during replay.
@@@ note
For more advanced schema evolution techniques refer to the @ref: [Persistence - Schema Evolution ](persistence-schema-evolution.md ) documentation.
2015-05-29 18:20:51 +02:00
2017-05-10 16:20:38 +02:00
@@@
2015-05-29 18:20:51 +02:00
2017-05-10 16:20:38 +02:00
## Custom serialization
2013-10-09 13:11:53 +02:00
2017-05-10 16:20:38 +02:00
Serialization of snapshots and payloads of `Persistent` messages is configurable with Akka's
@ref: [Serialization ](serialization.md ) infrastructure. For example, if an application wants to serialize
* payloads of type `MyPayload` with a custom `MyPayloadSerializer` and
* snapshots of type `MySnapshot` with a custom `MySnapshotSerializer`
2013-10-09 13:11:53 +02:00
it must add
2018-09-13 10:49:51 +02:00
@@snip [PersistenceSerializerDocSpec.scala ](/akka-docs/src/test/scala/docs/persistence/PersistenceSerializerDocSpec.scala ) { #custom -serializer-config }
2013-10-09 13:11:53 +02:00
Disable Java serialization by default, #22333 (#27285)
* akka.actor.allow-java-serialization = off
* Moved primitive (Long, Int, String, ByteString) serializers
from akka-remote to akka-actor since they had no dependency
and are useful also in local systems, e.g. persistence.
* e.g. needed for persistence-tck
* less allow-java-serialization=on in tests
* CborSerializable in Jackson/test module for ease of use
* JavaSerializable for Java serialization in tests, already in akka-testkit,
but misconfigured
* Made tests pass
* allow-java-serialization=on in akka-persistence
* allow-java-serialization=on in classic remoting tests
* JavaSerializable and CborSerializable in other remoting tests
* Added serialization for
* Boolean
* java.util.concurrent.TimeoutException, AskTimeoutException
* support for testing serialization with the inmem journal
* utility to verifySerialization, in SerializationTestKit
* remove AccountExampleWithCommandHandlersInState becuase not possible to serialize State when it's not static
* Effect() is factory in EventSourcedBehavior class
* test the account examples
* SharedLeveldbJournal.configToEnableJavaSerializationForTest
* support for exceptions from remote deployed child actors
* fallback to akka.remote.serialization.ThrowableNotSerializableException
if exception is not serializable when wrapped in system messages from
remote deployed child actors and Status.Failure messages
* it's implemented in `WrappedPayloadSupport.payloadBuilder`
* update reference documentation
* serialize-messages=off in most places, separate ticket for
improving or removing that feature
* migration guide, including description of rolling update
* fix 2.13 compiler error
* minor review feedback
2019-07-11 14:04:24 +02:00
to the application configuration. If not specified, an exception will be throw when trying to persist events or snapshots.
2013-10-09 13:11:53 +02:00
2017-05-10 16:20:38 +02:00
For more advanced schema evolution techniques refer to the @ref: [Persistence - Schema Evolution ](persistence-schema-evolution.md ) documentation.
2015-07-22 16:25:17 +02:00
2020-03-20 22:18:43 +09:00
## Testing with LevelDB journal
2013-12-06 12:48:44 +01:00
2021-06-02 13:37:52 +02:00
The LevelDB journal is deprecated and will be removed from a future Akka version, it is not advised to build new applications
with it. For testing the built in "inmem" journal or the actual journal that will be used in production of the application
is recommended. See @ref [Persistence Plugins ](persistence-plugins.md ) for some journal implementation choices.
2017-05-10 16:20:38 +02:00
When running tests with LevelDB default settings in `sbt` , make sure to set `fork := true` in your sbt project. Otherwise, you'll see an `UnsatisfiedLinkError` . Alternatively, you can switch to a LevelDB Java port by setting
2013-12-06 12:48:44 +01:00
2018-09-13 10:49:51 +02:00
@@snip [PersistencePluginDocSpec.scala ](/akka-docs/src/test/scala/docs/persistence/PersistencePluginDocSpec.scala ) { #native -config }
2013-12-06 12:48:44 +01:00
or
2018-09-13 10:49:51 +02:00
@@snip [PersistencePluginDocSpec.scala ](/akka-docs/src/test/scala/docs/persistence/PersistencePluginDocSpec.scala ) { #shared -store-native-config }
2013-12-06 12:48:44 +01:00
2021-06-02 13:37:52 +02:00
in your Akka configuration. Also note that for the LevelDB Java port, you will need the following dependencies:
2017-10-22 12:10:22 +02:00
2018-05-07 08:31:09 +01:00
@@dependency [sbt,Maven,Gradle] {
group="org.iq80.leveldb"
artifact="leveldb"
version="0.9"
}
2017-10-22 12:10:22 +02:00
2017-05-10 16:20:38 +02:00
@@@ warning
2019-09-26 22:48:12 +02:00
It is not possible to test persistence provided classes (i.e. `PersistentActor`
2019-10-01 15:29:12 +02:00
and @ref: [AtLeastOnceDelivery ](#at-least-once-delivery )) using `TestActorRef` due to its *synchronous* nature.
2017-05-10 16:20:38 +02:00
These traits need to be able to perform asynchronous tasks in the background in order to handle internal persistence
related events.
2017-05-11 17:27:57 +02:00
When testing Persistence based projects always rely on @ref: [asynchronous messaging using the TestKit ](testing.md#async-integration-testing ).
2015-08-14 12:12:20 +02:00
2017-05-10 16:20:38 +02:00
@@@
2015-08-14 12:12:20 +02:00
2017-05-10 16:20:38 +02:00
## Configuration
2014-03-23 18:39:55 +01:00
There are several configuration properties for the persistence module, please refer
2019-10-04 13:41:40 +02:00
to the @ref: [reference configuration ](general/configuration-reference.md#config-akka-persistence ).
2014-03-23 18:39:55 +01:00
2019-09-26 22:48:12 +02:00
The @ref: [journal and snapshot store plugins ](persistence-plugins.md ) have specific configuration, see
reference documentation of the chosen plugin.
2017-05-10 16:20:38 +02:00
## Multiple persistence plugin configurations
2015-02-08 19:12:02 -06:00
2017-07-26 00:26:17 -07:00
By default, a persistent actor will use the "default" journal and snapshot store plugins
2017-05-10 16:20:38 +02:00
configured in the following sections of the `reference.conf` configuration resource:
2015-02-08 19:12:02 -06:00
2018-09-13 10:49:51 +02:00
@@snip [PersistenceMultiDocSpec.scala ](/akka-docs/src/test/scala/docs/persistence/PersistenceMultiDocSpec.scala ) { #default -config }
2015-02-08 19:12:02 -06:00
2017-07-26 00:26:17 -07:00
Note that in this case the actor overrides only the `persistenceId` method:
2015-02-08 19:12:02 -06:00
2017-06-26 18:15:43 +09:00
Scala
2018-09-13 10:49:51 +02:00
: @@snip [PersistenceMultiDocSpec.scala ](/akka-docs/src/test/scala/docs/persistence/PersistenceMultiDocSpec.scala ) { #default -plugins }
2017-06-26 18:15:43 +09:00
Java
2018-09-13 10:49:51 +02:00
: @@snip [PersistenceMultiDocTest.java ](/akka-docs/src/test/java/jdocs/persistence/PersistenceMultiDocTest.java ) { #default -plugins }
2015-02-08 19:12:02 -06:00
2017-07-26 00:26:17 -07:00
When the persistent actor overrides the `journalPluginId` and `snapshotPluginId` methods,
the actor will be serviced by these specific persistence plugins instead of the defaults:
2015-02-08 19:12:02 -06:00
2017-06-26 18:15:43 +09:00
Scala
2018-09-13 10:49:51 +02:00
: @@snip [PersistenceMultiDocSpec.scala ](/akka-docs/src/test/scala/docs/persistence/PersistenceMultiDocSpec.scala ) { #override -plugins }
2017-06-26 18:15:43 +09:00
Java
2018-09-13 10:49:51 +02:00
: @@snip [PersistenceMultiDocTest.java ](/akka-docs/src/test/java/jdocs/persistence/PersistenceMultiDocTest.java ) { #override -plugins }
2015-02-08 19:12:02 -06:00
2017-05-10 16:20:38 +02:00
Note that `journalPluginId` and `snapshotPluginId` must refer to properly configured `reference.conf`
plugin entries with a standard `class` property as well as settings which are specific for those plugins, i.e.:
2015-02-08 19:12:02 -06:00
2018-09-13 10:49:51 +02:00
@@snip [PersistenceMultiDocSpec.scala ](/akka-docs/src/test/scala/docs/persistence/PersistenceMultiDocSpec.scala ) { #override -config }
2018-03-26 13:52:31 +02:00
## Give persistence plugin configurations at runtime
By default, a persistent actor will use the configuration loaded at `ActorSystem` creation time to create journal and snapshot store plugins.
When the persistent actor overrides the `journalPluginConfig` and `snapshotPluginConfig` methods,
the actor will use the declared `Config` objects with a fallback on the default configuration.
It allows a dynamic configuration of the journal and the snapshot store at runtime:
Scala
2018-09-13 10:49:51 +02:00
: @@snip [PersistenceMultiDocSpec.scala ](/akka-docs/src/test/scala/docs/persistence/PersistenceMultiDocSpec.scala ) { #runtime -config }
2018-03-26 13:52:31 +02:00
Java
2018-09-13 10:49:51 +02:00
: @@snip [PersistenceMultiDocTest.java ](/akka-docs/src/test/java/jdocs/persistence/PersistenceMultiDocTest.java ) { #runtime -config }
2018-08-22 10:47:32 +01:00
## See also
* @ref [Persistent FSM ](persistence-fsm.md )
2020-03-13 22:36:51 +01:00
* @ref [Persistence plugins ](persistence-plugins.md )
2018-08-22 10:47:32 +01:00
* @ref [Building a new storage backend ](persistence-journals.md )