Merge pull request #1915 from krasserm/wip-3704-persistence-improvements-part-1-krassserm

!per #3704 Persistence improvement (part 1)
This commit is contained in:
Patrik Nordwall 2014-01-17 06:37:52 -08:00
commit 8d2bc2bc40
55 changed files with 3474 additions and 2191 deletions

View file

@ -139,7 +139,7 @@ public class PersistenceDocTest {
if (message instanceof Persistent) {
Persistent p = (Persistent)message;
Persistent out = p.withPayload("done " + p.payload());
channel.tell(Deliver.create(out, destination), getSelf());
channel.tell(Deliver.create(out, destination.path()), getSelf());
}
}
}
@ -174,24 +174,35 @@ public class PersistenceDocTest {
.withRedeliverInterval(Duration.create(30, TimeUnit.SECONDS))
.withRedeliverMax(15)));
//#channel-custom-settings
//#channel-custom-listener
class MyListener extends UntypedActor {
@Override
public void onReceive(Object message) throws Exception {
if (message instanceof RedeliverFailure) {
Iterable<ConfirmablePersistent> messages =
((RedeliverFailure)message).getMessages();
// ...
}
}
}
final ActorRef myListener = getContext().actorOf(Props.create(MyListener.class));
getContext().actorOf(Channel.props(
ChannelSettings.create().withRedeliverFailureListener(null)));
//#channel-custom-listener
}
public void onReceive(Object message) throws Exception {
if (message instanceof Persistent) {
Persistent p = (Persistent)message;
Persistent out = p.withPayload("done " + p.payload());
channel.tell(Deliver.create(out, destination), getSelf());
channel.tell(Deliver.create(out, destination.path()), getSelf());
//#channel-example-reply
channel.tell(Deliver.create(out, getSender()), getSelf());
channel.tell(Deliver.create(out, getSender().path()), getSelf());
//#channel-example-reply
//#resolve-destination
channel.tell(Deliver.create(out, getSender(), Resolve.destination()), getSelf());
//#resolve-destination
//#resolve-sender
channel.tell(Deliver.create(out, destination, Resolve.sender()), getSender());
//#resolve-sender
}
}
}
@ -292,9 +303,13 @@ public class PersistenceDocTest {
.withRedeliverInterval(Duration.create(30, TimeUnit.SECONDS))
.withRedeliverMax(15)), "myPersistentChannel");
channel.tell(Deliver.create(Persistent.create("example"), destination), getSelf());
channel.tell(Deliver.create(Persistent.create("example"), destination.path()), getSelf());
//#persistent-channel-example
//#persistent-channel-watermarks
PersistentChannelSettings.create()
.withPendingConfirmationsMax(10000)
.withPendingConfirmationsMin(2000);
//#persistent-channel-watermarks
//#persistent-channel-reply
PersistentChannelSettings.create().withReplyPersistent(true);
//#persistent-channel-reply
@ -318,7 +333,7 @@ public class PersistenceDocTest {
// ...
// reliably deliver events
channel.tell(Deliver.create(Persistent.create(
event, getCurrentPersistentMessage()), destination), getSelf());
event, getCurrentPersistentMessage()), destination.path()), getSelf());
}
public void onReceiveReplay(Object msg) {
@ -339,4 +354,30 @@ public class PersistenceDocTest {
}
//#reliable-event-delivery
};
static Object o9 = new Object() {
//#view
class MyView extends UntypedView {
@Override
public String processorId() {
return "some-processor-id";
}
@Override
public void onReceive(Object message) throws Exception {
if (message instanceof Persistent) {
// ...
}
}
}
//#view
public void usage() {
final ActorSystem system = ActorSystem.create("example");
//#view-update
final ActorRef view = system.actorOf(Props.create(MyView.class));
view.tell(Update.create(true), null);
//#view-update
}
};
}

View file

@ -77,22 +77,32 @@ public class PersistencePluginDocTest {
class MyAsyncJournal extends AsyncWriteJournal {
@Override
public Future<Long> doReplayAsync(String processorId, long fromSequenceNr, long toSequenceNr, Procedure<PersistentRepr> replayCallback) {
public Future<Void> doAsyncWriteMessages(Iterable<PersistentRepr> messages) {
return null;
}
@Override
public Future<Void> doWriteAsync(Iterable<PersistentRepr> persistentBatch) {
public Future<Void> doAsyncWriteConfirmations(Iterable<PersistentConfirmation> confirmations) {
return null;
}
@Override
public Future<Void> doDeleteAsync(String processorId, long fromSequenceNr, long toSequenceNr, boolean permanent) {
public Future<Void> doAsyncDeleteMessages(Iterable<PersistentId> messageIds, boolean permanent) {
return null;
}
@Override
public Future<Void> doConfirmAsync(String processorId, long sequenceNr, String channelId) {
public Future<Void> doAsyncDeleteMessagesTo(String processorId, long toSequenceNr, boolean permanent) {
return null;
}
@Override
public Future<Void> doAsyncReplayMessages(String processorId, long fromSequenceNr, long toSequenceNr, long max, Procedure<PersistentRepr> replayCallback) {
return null;
}
@Override
public Future<Long> doAsyncReadHighestSequenceNr(String processorId, long fromSequenceNr) {
return null;
}
}

View file

@ -5,21 +5,13 @@ Persistence
###########
Akka persistence enables stateful actors to persist their internal state so that it can be recovered when an actor
is started, restarted by a supervisor or migrated in a cluster. It also allows stateful actors to recover from JVM
crashes, for example. The key concept behind Akka persistence is that only changes to an actor's internal state are
persisted but never its current state directly (except for optional snapshots). These changes are only ever appended
to storage, nothing is ever mutated, which allows for very high transaction rates and efficient replication. Stateful
actors are recovered by replaying stored changes to these actors from which they can rebuild internal state. This can
be either the full history of changes or starting from a snapshot of internal actor state which can dramatically
reduce recovery times. Akka persistence also provides point-to-point communication channels with at-least-once
message delivery guarantees.
Storage backends for state changes and snapshots are pluggable in Akka persistence. Currently, these are written to
the local filesystem. Distributed and replicated storage, with the possibility of scaling writes, will be available
soon.
Akka persistence is inspired by the `eventsourced`_ library. It follows the same concepts and architecture of
`eventsourced`_ but significantly differs on API and implementation level.
is started, restarted after a JVM crash or by a supervisor, or migrated in a cluster. The key concept behind Akka
persistence is that only changes to an actor's internal state are persisted but never its current state directly
(except for optional snapshots). These changes are only ever appended to storage, nothing is ever mutated, which
allows for very high transaction rates and efficient replication. Stateful actors are recovered by replaying stored
changes to these actors from which they can rebuild internal state. This can be either the full history of changes
or starting from a snapshot which can dramatically reduce recovery times. Akka persistence also provides point-to-point
communication channels with at-least-once message delivery semantics.
.. warning::
@ -28,6 +20,9 @@ Akka persistence is inspired by the `eventsourced`_ library. It follows the same
changes to a minimum the binary compatibility guarantee for maintenance releases does not apply to the
contents of the ``akka.persistence`` package.
Akka persistence is inspired by the `eventsourced`_ library. It follows the same concepts and architecture of
`eventsourced`_ but significantly differs on API and implementation level.
.. _eventsourced: https://github.com/eligosource/eventsourced
Dependencies
@ -48,16 +43,22 @@ Architecture
before its ``onReceive`` method is called. When a processor is started or restarted, journaled messages are replayed
to that processor, so that it can recover internal state from these messages.
* *Channel*: Channels are used by processors to communicate with other actors. They prevent that replayed messages
are redundantly delivered to these actors and provide at-least-once message delivery guarantees, also in case of
sender and receiver JVM crashes.
* *View*: A view is a persistent, stateful actor that receives journaled messages that have been written by another
processor. A view itself does not journal new messages, instead, it updates internal state only from a processor's
replicated message stream.
* *Channel*: Channels are used by processors and views to communicate with other actors. They prevent that replayed
messages are redundantly delivered to these actors and provide at-least-once message delivery semantics, also in
case of sender and receiver JVM crashes.
* *Journal*: A journal stores the sequence of messages sent to a processor. An application can control which messages
are stored and which are received by the processor without being journaled. The storage backend of a journal is
pluggable.
are journaled and which are received by the processor without being journaled. The storage backend of a journal is
pluggable. The default journal storage plugin writes to the local filesystem, replicated journals are available as
:ref:`community-projects-java`.
* *Snapshot store*: A snapshot store persists snapshots of a processor's internal state. Snapshots are used for
optimizing recovery times. The storage backend of a snapshot store is pluggable.
* *Snapshot store*: A snapshot store persists snapshots of a processor's or a view's internal state. Snapshots are
used for optimizing recovery times. The storage backend of a snapshot store is pluggable. The default snapshot
storage plugin writes to the local filesystem.
* *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-java`)
@ -75,10 +76,9 @@ A processor can be implemented by extending the abstract ``UntypedProcessor`` cl
Processors only write messages of type ``Persistent`` to the journal, others are received without being persisted.
When a processor's ``onReceive`` method is called with a ``Persistent`` message it can safely assume that this message
has been successfully written to the journal. If a journal fails to write a ``Persistent`` message then the processor
is stopped, by default. If an application wants that a processors continues to run on persistence failures it must
handle ``PersistenceFailure`` messages. In this case, a processor may want to inform the sender about the failure,
so that the sender can re-send the message, if needed, under the assumption that the journal recovered from a
temporary failure.
is stopped, by default. If a processor should continue running on persistence failures it must handle
``PersistenceFailure`` messages. In this case, a processor may want to inform the sender about the failure,
so that the sender can re-send the message, if needed.
An ``UntypedProcessor`` itself is an ``Actor`` and can therefore be instantiated with ``actorOf``.
@ -87,9 +87,9 @@ An ``UntypedProcessor`` itself is an ``Actor`` and can therefore be instantiated
Recovery
--------
By default, a processor is automatically recovered on start and on restart by replaying persistent messages.
By default, a processor is automatically recovered on start and on restart by replaying journaled messages.
New messages sent to a processor during recovery do not interfere with replayed messages. New messages will
only be received by that processor after recovery completes.
only be received by a processor after recovery completes.
Recovery customization
^^^^^^^^^^^^^^^^^^^^^^
@ -137,7 +137,7 @@ that message as argument. An optional ``permanent`` parameter specifies whether
deleted from the journal or only marked as deleted. In both cases, the message won't be replayed. Later extensions
to Akka persistence will allow to replay messages that have been marked as deleted which can be useful for debugging
purposes, for example. To delete all messages (journaled by a single processor) up to a specified sequence number,
processors can call the ``deleteMessages`` method.
processors should call the ``deleteMessages`` method.
Identifiers
-----------
@ -150,41 +150,103 @@ method.
Applications can customize a processor's id by specifying an actor name during processor creation as shown in
section :ref:`processors-java`. This changes that processor's name in its actor hierarchy and hence influences only
part of the processor id. To fully customize a processor's id, the ``processorId`` method should be overridden.
part of the processor id. To fully customize a processor's id, the ``processorId`` method must be overridden.
.. includecode:: code/docs/persistence/PersistenceDocTest.java#processor-id-override
Overriding ``processorId`` is the recommended way to generate stable identifiers.
.. _views-java:
Views
=====
Views can be implemented by extending the ``UntypedView`` trait and implementing the ``onReceive`` and the ``processorId``
methods.
.. includecode:: code/docs/persistence/PersistenceDocTest.java#view
The ``processorId`` identifies the processor from which the view receives journaled messages. It is not necessary
the referenced processor is actually running. Views read messages from a processor's journal directly. When a
processor is started later and begins to write new messages, the corresponding view is updated automatically, by
default.
Updates
-------
The default update interval of all views of an actor system is configurable:
.. includecode:: ../scala/code/docs/persistence/PersistenceDocSpec.scala#auto-update-interval
``View`` implementation classes may also override the ``autoUpdateInterval`` method to return a custom update
interval for a specific view class or view instance. Applications may also trigger additional updates at
any time by sending a view an ``Update`` message.
.. includecode:: code/docs/persistence/PersistenceDocTest.java#view-update
If the ``await`` parameter is set to ``true``, messages that follow the ``Update`` request are processed when the
incremental message replay, triggered by that update request, completed. If set to ``false`` (default), messages
following the update request may interleave with the replayed message stream. Automated updates always run with
``await = false``.
Automated updates of all views of an actor system can be turned off by configuration:
.. includecode:: ../scala/code/docs/persistence/PersistenceDocSpec.scala#auto-update
Implementation classes may override the configured default value by overriding the ``autoUpdate`` method. To
limit the number of replayed messages per update request, applications can configure a custom
``akka.persistence.view.auto-update-replay-max`` value or override the ``autoUpdateReplayMax`` method. The number
of replayed messages for manual updates can be limited with the ``replayMax`` parameter of the ``Update`` message.
Recovery
--------
Initial recovery of views works in the very same way as for :ref:`processors` (i.e. by sending a ``Recover`` message
to self). The maximum number of replayed messages during initial recovery is determined by ``autoUpdateReplayMax``.
Further possibilities to customize initial recovery are explained in section :ref:`processors-java`.
Identifiers
-----------
A view must have an identifier that doesn't change across different actor incarnations. It defaults to the
``String`` representation of the actor path without the address part and can be obtained via the ``viewId``
method.
Applications can customize a view's id by specifying an actor name during view creation. This changes that view's
name in its actor hierarchy and hence influences only part of the view id. To fully customize a view's id, the
``viewId`` method must be overridden. Overriding ``viewId`` is the recommended way to generate stable identifiers.
The ``viewId`` must differ from the referenced ``processorId``, unless :ref:`snapshots-java` of a view and its
processor shall be shared (which is what applications usually do not want).
.. _channels-java:
Channels
========
.. warning::
Channels are special actors that are used by processors or views to communicate with other actors (channel
destinations). The following discusses channels in context of processors but this is also applicable to views.
There are further changes planned to the channel API that couldn't make it into the current milestone.
One example is to have only a single destination per channel to allow gap detection and more advanced
flow control.
Channels are special actors that are used by processors to communicate with other actors (channel destinations).
Channels prevent redundant delivery of replayed messages to destinations during processor recovery. A replayed
message is retained by a channel if its previous delivery has been confirmed by a destination.
message is retained by a channel if its delivery has been confirmed by a destination.
.. includecode:: code/docs/persistence/PersistenceDocTest.java#channel-example
A channel is ready to use once it has been created, no recovery or further activation is needed. A ``Deliver``
request instructs a channel to send a ``Persistent`` message to a destination. Sender references are preserved
by a channel, therefore, a destination can reply to the sender of a ``Deliver`` request.
request instructs a channel to send a ``Persistent`` message to a destination. A destination is provided as
``ActorPath`` and messages are sent by the channel via that path's ``ActorSelection``. Sender references are
preserved by a channel, therefore, a destination can reply to the sender of a ``Deliver`` request.
If a processor wants to reply to a ``Persistent`` message sender it should use the ``getSender()`` reference as
If a processor wants to reply to a ``Persistent`` message sender it should use the ``getSender()`` path as
channel destination.
.. includecode:: code/docs/persistence/PersistenceDocTest.java#channel-example-reply
Persistent messages delivered by a channel are of type ``ConfirmablePersistent``. ``ConfirmablePersistent`` extends
``Persistent`` by adding the methods ``confirm`` method and ``redeliveries`` (see also :ref:`redelivery-java`). Channel
destinations confirm the delivery of a ``ConfirmablePersistent`` message by calling ``confirm()`` an that message.
This asynchronously writes a confirmation entry to the journal. Replayed messages internally contain these confirmation
entries which allows a channel to decide if a message should be retained or not.
``Persistent`` by adding the methods ``confirm`` and ``redeliveries`` (see also :ref:`redelivery-java`). A channel
destination confirms the delivery of a ``ConfirmablePersistent`` message by calling ``confirm()`` on that message.
This asynchronously writes a confirmation entry to the journal. Replayed messages internally contain confirmation
entries which allows a channel to decide if it should retain these messages or not.
A ``Processor`` can also be used as channel destination i.e. it can persist ``ConfirmablePersistent`` messages too.
@ -193,25 +255,23 @@ A ``Processor`` can also be used as channel destination i.e. it can persist ``Co
Message re-delivery
-------------------
Channels re-deliver messages to destinations if they do not confirm their receipt within a configurable timeout.
Channels re-deliver messages to destinations if they do not confirm delivery within a configurable timeout.
This timeout can be specified as ``redeliverInterval`` when creating a channel, optionally together with the
maximum number of re-deliveries a channel should attempt for each unconfirmed message.
maximum number of re-deliveries a channel should attempt for each unconfirmed message. The number of re-delivery
attempts can be obtained via the ``redeliveries`` method on ``ConfirmablePersistent``.
.. includecode:: code/docs/persistence/PersistenceDocTest.java#channel-custom-settings
Message re-delivery is done out of order with regards to normal delivery i.e. redelivered messages may arrive
later than newer normally delivered messages. The number of re-delivery attempts can be obtained via the
``redeliveries`` method on ``ConfirmablePersistent``.
A channel keeps messages in memory until their successful delivery has been confirmed or the maximum number of
re-deliveries is reached. To be notified about messages that have reached the maximum number of re-deliveries,
applications can register a listener at channel creation.
A channel keeps messages in memory until their successful delivery has been confirmed by their destination(s)
or their maximum number of re-deliveries is reached. In the latter case, the application has to re-send the
correspnding ``Deliver`` request to the channel so that the channel can start a new series of delivery attempts
(starting again with a ``redeliveries`` count of ``0``).
.. includecode:: code/docs/persistence/PersistenceDocTest.java#channel-custom-listener
Re-sending ``Deliver`` requests is done automatically if the sending processor replays messages: only ``Deliver``
requests of unconfirmed messages will be served again by the channel. A message replay can be enforced by an
application by restarting the sending processor, for example. A replay will also take place if the whole
application is restarted, either after normal termination or after a crash.
A listener receives ``RedeliverFailure`` notifications containing all messages that could not be delivered. On
receiving a ``RedeliverFailure`` message, an application may decide to restart the sending processor to enforce
a re-send of these messages to the channel or confirm these messages to prevent further re-sends. The sending
processor can also be restarted any time later to re-send unconfirmed messages.
This combination of
@ -220,7 +280,7 @@ This combination of
* message re-deliveries by channels and
* application-level confirmations (acknowledgements) by destinations
enables channels to provide at-least-once message delivery guarantees. Possible duplicates can be detected by
enables channels to provide at-least-once message delivery semantics. Possible duplicates can be detected by
destinations by tracking message sequence numbers. Message sequence numbers are generated per sending processor.
Depending on how a processor routes outbound messages to destinations, they may either see a contiguous message
sequence or a sequence with gaps.
@ -229,14 +289,13 @@ sequence or a sequence with gaps.
If a processor emits more than one outbound message per inbound ``Persistent`` message it **must** use a
separate channel for each outbound message to ensure that confirmations are uniquely identifiable, otherwise,
at-least-once message delivery is not guaranteed. This rule has been introduced to avoid writing additional
at-least-once message delivery semantics do not apply. This rule has been introduced to avoid writing additional
outbound message identifiers to the journal which would decrease the overall throughput. It is furthermore
recommended to collapse multiple outbound messages to the same destination into a single outbound message,
otherwise, if sent via multiple channels, their ordering is not defined. These restrictions are likely to be
removed in the final release.
otherwise, if sent via multiple channels, their ordering is not defined.
Whenever an application wants to have more control how sequence numbers are assigned to messages it should use
an application-specific sequence number generator and include the generated sequence numbers into the ``payload``
If an application wants to have more control how sequence numbers are assigned to messages it should use an
application-specific sequence number generator and include the generated sequence numbers into the ``payload``
of ``Persistent`` messages.
Persistent channels
@ -246,60 +305,45 @@ Channels created with ``Channel.props`` do not persist messages. These channels
with a sending processor that takes care of persistence, hence, channel-specific persistence is not necessary in
this case. They are referred to as transient channels in the following.
Applications may also use transient channels standalone (i.e. without a sending processor) if re-delivery attempts
to destinations are required but message loss in case of a sender JVM crash is not an issue. If applications want to
use standalone channels but message loss is not acceptable, they should use persistent channels. A persistent channel
can be created with ``PersistentChannel.props`` and configured with a ``PersistentChannelSettings`` object.
Persistent channels are like transient channels but additionally persist messages before delivering them. Messages
that have been persisted by a persistent channel are deleted when destinations confirm their delivery. A persistent
channel can be created with ``PersistentChannel.props`` and configured with a ``PersistentChannelSettings`` object.
.. includecode:: code/docs/persistence/PersistenceDocTest.java#persistent-channel-example
A persistent channel is like a transient channel that additionally persists ``Deliver`` requests before serving it.
Hence, it can recover from sender JVM crashes and provide the same message re-delivery semantics as a transient
channel in combination with an application-defined processor.
A persistent channel is useful for delivery of messages to slow destinations or destinations that are unavailable
for a long time. It can constrain the number of pending confirmations based on the ``pendingConfirmationsMax``
and ``pendingConfirmationsMin`` parameters of ``PersistentChannelSettings``.
By default, a persistent channel doesn't reply whether a ``Persistent`` message, sent with ``Deliver``, has been
successfully persisted or not. This can be enabled by creating the channel with the ``replyPersistent`` configuration
parameter set to ``true``:
.. includecode:: code/docs/persistence/PersistenceDocTest.java#persistent-channel-watermarks
It suspends delivery when the number of pending confirmations reaches ``pendingConfirmationsMax`` and resumes
delivery again when this number falls below ``pendingConfirmationsMin``. This prevents both, flooding destinations
with more messages than they can process and unlimited memory consumption by the channel. A persistent channel
continues to persist new messages even when message delivery is temporarily suspended.
Standalone usage
----------------
Applications may also use channels standalone. Transient channels can be used standalone if re-delivery attempts
to destinations are required but message loss in case of a sender JVM crash is not an issue. If message loss in
case of a sender JVM crash is an issue, persistent channels should be used. In this case, applications may want to
receive replies from the channel whether messages have been successfully persisted or not. This can be enabled by
creating the channel with the ``replyPersistent`` configuration parameter set to ``true``:
.. includecode:: code/docs/persistence/PersistenceDocTest.java#persistent-channel-reply
With this setting, either the successfully persisted message is replied to the sender or a ``PersistenceFailure``.
In case of a persistence failure, the sender should re-send the message.
Using a persistent channel in combination with an application-defined processor can make sense if destinations are
unavailable for a long time and an application doesn't want to buffer all messages in memory (but write them to the
journal only). In this case, delivery can be disabled by sending the channel a ``DisableDelivery`` message (to
stop delivery and persist-only) and re-enabled again by sending it an ``EnableDelivery`` message. A disabled channel
that receives an ``EnableDelivery`` message, processes all persisted, unconfirmed ``Deliver`` requests again before
serving new ones.
Sender resolution
-----------------
``ActorRef`` s of ``Persistent`` message senders are also stored in the journal. Consequently, they may become invalid if
an application is restarted and messages are replayed. For example, the stored ``ActorRef`` may then reference
a previous incarnation of a sender and a new incarnation of that sender cannot receive a reply from a processor.
This may be acceptable for many applications but others may require that a new sender incarnation receives the
reply (to reliably resume a conversation between actors after a JVM crash, for example). Here, a channel may
assist in resolving new sender incarnations by specifying a third ``Deliver`` argument:
* ``Resolve.destination()`` if the sender of a persistent message is used as channel destination
.. includecode:: code/docs/persistence/PersistenceDocTest.java#resolve-destination
* ``Resolve.sender()`` if the sender of a persistent message is forwarded to a destination.
.. includecode:: code/docs/persistence/PersistenceDocTest.java#resolve-sender
Default is ``Resolve.off()`` which means no resolution. Find out more in the ``Deliver`` API docs.
With this setting, either the successfully persisted message is replied to the sender or a ``PersistenceFailure``
message. In case the latter case, the sender should re-send the message.
Identifiers
-----------
In the same way as :ref:`processors`, channels also have an identifier that defaults to a channel's path. A channel
identifier can therefore be customized by using a custom actor name at channel creation. This changes that channel's
name in its actor hierarchy and hence influences only part of the channel identifier. To fully customize a channel
identifier, it should be provided as argument ``Channel.props(String)`` or ``PersistentChannel.props(String)``.
In the same way as :ref:`processors-java` and :ref:`views-java`, channels also have an identifier that defaults to a channel's
path. A channel identifier can therefore be customized by using a custom actor name at channel creation. This changes
that channel's name in its actor hierarchy and hence influences only part of the channel identifier. To fully customize
a channel identifier, it should be provided as argument ``Channel.props(String)`` or ``PersistentChannel.props(String)``
(recommended to generate stable identifiers).
.. includecode:: code/docs/persistence/PersistenceDocTest.java#channel-id-override
@ -326,16 +370,18 @@ Sequence number
The sequence number of a ``Persistent`` message can be obtained via its ``sequenceNr`` method. Persistent
messages are assigned sequence numbers on a per-processor basis (or per channel basis if used
standalone). A sequence starts at ``1L`` and doesn't contain gaps unless a processor deletes a message.
standalone). A sequence starts at ``1L`` and doesn't contain gaps unless a processor deletes messages.
.. _snapshots-java:
Snapshots
=========
Snapshots can dramatically reduce recovery times. Processors can save snapshots of internal state by calling the
``saveSnapshot`` method on ``Processor``. If saving of a snapshot succeeds, the processor will receive a
``SaveSnapshotSuccess`` message, otherwise a ``SaveSnapshotFailure`` message.
Snapshots can dramatically reduce recovery times of processors and views. The following discusses snapshots
in context of processors but this is also applicable to views.
Processors can save snapshots of internal state by calling the ``saveSnapshot`` method. If saving of a snapshot
succeeds, the processor receives a ``SaveSnapshotSuccess`` message, otherwise a ``SaveSnapshotFailure`` message
.. includecode:: code/docs/persistence/PersistenceDocTest.java#save-snapshot
@ -359,9 +405,9 @@ saved snapshot matches the specified ``SnapshotSelectionCriteria`` will replay a
Snapshot deletion
-----------------
A processor can delete a single snapshot by calling the ``deleteSnapshot`` method with the sequence number and the
timestamp of the snapshot as argument. To bulk-delete snapshots that match a specified ``SnapshotSelectionCriteria``
argument, processors can call the ``deleteSnapshots`` method.
A processor can delete individual snapshots by calling the ``deleteSnapshot`` method with the sequence number and the
timestamp of a snapshot as argument. To bulk-delete snapshots matching ``SnapshotSelectionCriteria``, processors should
use the ``deleteSnapshots`` method.
.. _event-sourcing-java:
@ -389,7 +435,7 @@ Akka persistence supports event sourcing with the abstract ``UntypedEventsourced
event sourcing as a pattern on top of command sourcing). A processor that extends this abstract class does not handle
``Persistent`` messages directly but uses the ``persist`` method to persist and handle events. The behavior of an
``UntypedEventsourcedProcessor`` is defined by implementing ``onReceiveReplay`` and ``onReceiveCommand``. This is
best explained with an example (which is also part of ``akka-sample-persistence``).
demonstrated in the following example.
.. includecode:: ../../../akka-samples/akka-sample-persistence/src/main/java/sample/persistence/japi/EventsourcedExample.java#eventsourced-example
@ -402,28 +448,25 @@ a command is handled by generating two events which are then persisted and handl
``persist`` with an event (or a sequence of events) as first argument and an event handler as second argument.
The ``persist`` method persists events asynchronously and the event handler is executed for successfully persisted
events. Successfully persisted events are internally sent back to the processor as separate messages which trigger
the event handler execution. An event handler may therefore close over processor 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).
events. Successfully persisted events are internally sent back to the processor as individual messages that trigger
event handler executions. An event handler may close over processor 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 processor state using event data and notifying others
about successful state changes by publishing events.
When persisting events with ``persist`` it is guaranteed that the processor will not receive new commands between
When persisting events with ``persist`` it is guaranteed that the processor will not receive further commands between
the ``persist`` call and the execution(s) of the associated event handler. This also holds for multiple ``persist``
calls in context of a single command.
The example also demonstrates how to change the processor's default behavior, defined by ``onReceiveCommand``, to
another behavior, defined by ``otherCommandHandler``, and back using ``getContext().become()`` and
``getContext().unbecome()``. See also the API docs of ``persist`` for further details.
calls in context of a single command. The example also shows how to switch between command different command handlers
with ``getContext().become()`` and ``getContext().unbecome()``.
Reliable event delivery
-----------------------
Sending events from an event handler to another actor directly doesn't guarantee delivery of these events. To
guarantee at-least-once delivery, :ref:`channels-java` must be used. In this case, also replayed events (received by
``receiveReplay``) must be sent to a channel, as shown in the following example:
Sending events from an event handler to another actor has at-most-once delivery semantics. For at-least-once delivery,
:ref:`channels-java` must be used. In this case, also replayed events (received by ``receiveReplay``) must be sent to a
channel, as shown in the following example:
.. includecode:: code/docs/persistence/PersistenceDocTest.java#reliable-event-delivery
@ -438,29 +481,33 @@ To optimize throughput, an ``UntypedProcessor`` internally batches received ``Pe
writing them to the journal (as a single batch). The batch size dynamically grows from 1 under low and moderate loads
to a configurable maximum size (default is ``200``) under high load.
.. includecode:: ../scala/code/docs/persistence/PersistencePluginDocSpec.scala#max-batch-size
.. includecode:: ../scala/code/docs/persistence/PersistencePluginDocSpec.scala#max-message-batch-size
A new batch write is triggered by a processor as soon as a batch reaches the maximum size or if the journal completed
writing the previous batch. Batch writes are never timer-based which keeps latencies as low as possible.
writing the previous batch. Batch writes are never timer-based which keeps latencies at a minimum.
Applications that want to have more explicit control over batch writes and batch sizes can send processors
``PersistentBatch`` messages.
.. includecode:: code/docs/persistence/PersistenceDocTest.java#batch-write
``Persistent`` messages contained in a ``PersistentBatch`` message are always written atomically, even if the batch
size is greater than ``max-batch-size``. Also, a ``PersistentBatch`` is written isolated from other batches.
``Persistent`` messages contained in a ``PersistentBatch`` are always written atomically, even if the batch
size is greater than ``max-message-batch-size``. Also, a ``PersistentBatch`` is written isolated from other batches.
``Persistent`` messages contained in a ``PersistentBatch`` are received individually by a processor.
``PersistentBatch`` messages, for example, are used internally by an ``UntypedEventsourcedProcessor`` to ensure atomic
writes of events. All events that are persisted in context of a single command are written as single batch to the
writes of events. All events that are persisted in context of a single command are written as a single batch to the
journal (even if ``persist`` is called multiple times per command). The recovery of an ``UntypedEventsourcedProcessor``
will therefore never be done partially i.e. with only a subset of events persisted by a single command.
will therefore never be done partially (with only a subset of events persisted by a single command).
Confirmation and deletion operations performed by :ref:`channels-java` are also batched. The maximum confirmation
and deletion batch sizes are configurable with ``akka.persistence.journal.max-confirmation-batch-size`` and
``akka.persistence.journal.max-deletion-batch-size``, respectively.
Storage plugins
===============
Storage backends for journals and snapshot stores are plugins in akka-persistence. The default journal plugin
Storage backends for journals and snapshot stores are pluggable in Akka persistence. The default journal plugin
writes messages to LevelDB (see :ref:`local-leveldb-journal-java`). The default snapshot store plugin writes snapshots
as individual files to the local filesystem (see :ref:`local-snapshot-store-java`). Applications can provide their own
plugins by implementing a plugin API and activate them by configuration. Plugin development requires the following
@ -472,19 +519,19 @@ Journal plugin API
------------------
A journal plugin either extends ``SyncWriteJournal`` or ``AsyncWriteJournal``. ``SyncWriteJournal`` is an
actor that should be extended when the storage backend API only supports synchronous, blocking writes. The
methods to be implemented in this case are:
actor that should be extended when the storage backend API only supports synchronous, blocking writes. In this
case, the methods to be implemented are:
.. includecode:: ../../../akka-persistence/src/main/java/akka/persistence/journal/japi/SyncWritePlugin.java#sync-write-plugin-api
``AsyncWriteJournal`` is an actor that should be extended if the storage backend API supports asynchronous,
non-blocking writes. The methods to be implemented in that case are:
non-blocking writes. In this case, the methods to be implemented are:
.. includecode:: ../../../akka-persistence/src/main/java/akka/persistence/journal/japi/AsyncWritePlugin.java#async-write-plugin-api
Message replays are always asynchronous, therefore, any journal plugin must implement:
Message replays and sequence number recovery are always asynchronous, therefore, any journal plugin must implement:
.. includecode:: ../../../akka-persistence/src/main/java/akka/persistence/journal/japi/AsyncReplayPlugin.java#async-replay-plugin-api
.. includecode:: ../../../akka-persistence/src/main/java/akka/persistence/journal/japi/AsyncRecoveryPlugin.java#async-replay-plugin-api
A journal plugin can be activated with the following minimal configuration:
@ -530,15 +577,15 @@ Shared LevelDB journal
----------------------
A LevelDB instance can also be shared by multiple actor systems (on the same or on different nodes). This, for
example, allows processors to failover to a backup node, assuming that the node, where the shared instance is
runnning, is accessible from the backup node.
example, allows processors to failover to a backup node and continue using the shared journal instance from the
backup node.
.. warning::
A shared LevelDB instance is a single point of failure and should therefore only be used for testing
purposes.
purposes. Highly-available, replicated journal are available as :ref:`community-projects-java`.
A shared LevelDB instance can be created by instantiating the ``SharedLeveldbStore`` actor.
A shared LevelDB instance is started by instantiating the ``SharedLeveldbStore`` actor.
.. includecode:: code/docs/persistence/PersistencePluginDocTest.java#shared-store-creation
@ -565,12 +612,21 @@ i.e. only the first injection is used.
Local snapshot store
--------------------
The default snapshot store plugin is ``akka.persistence.snapshot-store.local`` which writes snapshot files to
The default snapshot store plugin is ``akka.persistence.snapshot-store.local``. It writes snapshot files to
the local filesystem. The default storage location is a directory named ``snapshots`` in the current working
directory. This can be changed by configuration where the specified path can be relative or absolute:
.. includecode:: ../scala/code/docs/persistence/PersistencePluginDocSpec.scala#snapshot-config
.. _community-projects-java:
Community plugins
-----------------
* `Replicated journal backed by Apache Cassandra <https://github.com/krasserm/akka-persistence-cassandra/>`_.
* `Replicated journal backed by Apache HBase <https://github.com/ktoso/akka-persistence-hbase/>`_.
* `Replicated journal backed by MongoDB <https://github.com/ddevore/akka-persistence-mongo/>`_.
Custom serialization
====================
@ -584,8 +640,7 @@ it must add
.. includecode:: ../scala/code/docs/persistence/PersistenceSerializerDocSpec.scala#custom-serializer-config
to the application configuration. If not specified, a default serializer is used, which is the ``JavaSerializer``
in this example.
to the application configuration. If not specified, a default serializer is used.
Testing
=======
@ -599,5 +654,4 @@ or
.. includecode:: ../scala/code/docs/persistence/PersistencePluginDocSpec.scala#shared-store-native-config
in your Akka configuration. The latter setting applies if you're using a :ref:`shared-leveldb-journal-java`. The LevelDB
Java port is for testing purposes only.
in your Akka configuration. The LevelDB Java port is for testing purposes only.