diff --git a/akka-docs/rst/java/code/docs/persistence/PersistenceDocTest.java b/akka-docs/rst/java/code/docs/persistence/PersistenceDocTest.java index 60e1376338..3b8bff72a4 100644 --- a/akka-docs/rst/java/code/docs/persistence/PersistenceDocTest.java +++ b/akka-docs/rst/java/code/docs/persistence/PersistenceDocTest.java @@ -86,7 +86,7 @@ public class PersistenceDocTest { }; static Object o2 = new Object() { - abstract class MyProcessor1 extends UntypedProcessor { + abstract class MyProcessor1 extends UntypedPersistentActor { //#recover-on-start-disabled @Override public void preStart() {} @@ -98,7 +98,7 @@ public class PersistenceDocTest { //#recover-on-restart-disabled } - abstract class MyProcessor2 extends UntypedProcessor { + abstract class MyProcessor2 extends UntypedPersistentActor { //#recover-on-start-custom @Override public void preStart() { @@ -107,7 +107,7 @@ public class PersistenceDocTest { //#recover-on-start-custom } - abstract class MyProcessor3 extends UntypedProcessor { + abstract class MyProcessor3 extends UntypedPersistentActor { //#deletion @Override public void preRestart(Throwable reason, Option message) { @@ -119,7 +119,7 @@ public class PersistenceDocTest { //#deletion } - class MyProcessor4 extends UntypedProcessor implements ProcessorMethods { + class MyProcessor4 extends UntypedPersistentActor implements ProcessorMethods { //#persistence-id-override @Override public String persistenceId() { @@ -127,15 +127,25 @@ public class PersistenceDocTest { } //#persistence-id-override @Override - public void onReceive(Object message) throws Exception {} + public void onReceiveRecover(Object message) throws Exception {} + @Override + public void onReceiveCommand(Object message) throws Exception {} } - class MyProcessor5 extends UntypedProcessor { + class MyProcessor5 extends UntypedPersistentActor { //#recovery-completed - public void onReceive(Object message) throws Exception { + + @Override + public void onReceiveRecover(Object message) { + // ... + } + + @Override + public void onReceiveCommand(Object message) throws Exception { if (message instanceof RecoveryCompleted) { recoveryCompleted(); - getContext().become(active); + } else if (message instanceof String) { + // ... } else { unhandled(message); } @@ -146,17 +156,6 @@ public class PersistenceDocTest { // ... } - Procedure active = new Procedure() { - @Override - public void apply(Object message) { - if (message instanceof Persistent) { - // ... - } - else { - unhandled(message); - } - } - }; //#recovery-completed } }; diff --git a/akka-docs/rst/java/lambda-persistence.rst b/akka-docs/rst/java/lambda-persistence.rst index a8c77b5e98..7d7d704319 100644 --- a/akka-docs/rst/java/lambda-persistence.rst +++ b/akka-docs/rst/java/lambda-persistence.rst @@ -12,7 +12,7 @@ persistence is that only changes to an actor's internal state are persisted but 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. +communication with at-least-once message delivery semantics. .. warning:: @@ -40,29 +40,28 @@ Akka persistence is a separate jar file. Make sure that you have the following d Architecture ============ -* *Processor* (deprecated, use *PersistentActor* instead): A processor is a persistent, stateful actor. Messages sent - to a processor are written to a journal 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. - -* *PersistentActor*: Is a persistent, stateful actor. It is able to persist events to a journal and can react to +* *AbstractPersistentActor*: Is a persistent, stateful actor. It is able to persist events to a journal and can react to 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 recover internal state from these messages. * *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 + persistent actor. A view itself does not journal new messages, instead, it updates internal state only from a persistent actor's replicated message stream. + +* *Streams*: Messages written by a persistent actor can be published in compliance with the `Reactive Streams`_ specification. + Only those messages that are explicitly requested from downstream persistent actors are actually pulled from a persistent actor's + journal. -* *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 +* *AbstractPersistentActorAtLeastOnceDelivery*: To send messages with at-least-once delivery semantics to destinations, 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 journaled and which are received by the processor without being journaled. The storage backend of a journal is +* *Journal*: 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. The storage backend of a journal is pluggable. The default journal storage plugin writes to the local filesystem, replicated journals are available as `Community plugins`_. -* *Snapshot store*: A snapshot store persists snapshots of a processor's or a view's internal state. Snapshots are +* *Snapshot store*: A snapshot store persists snapshots of a persistent actor'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. @@ -70,34 +69,87 @@ Architecture development of event sourced applications (see section :ref:`event-sourcing-java-lambda`) .. _Community plugins: https://gist.github.com/krasserm/8612920#file-akka-persistence-plugins-md +.. _Reactive Streams: http://www.reactive-streams.org/ -.. _processors-lambda-java: +.. _event-sourcing-java-lambda: -Processors -========== +Event sourcing +============== -A processor can be implemented by extending ``AbstractProcessor`` class and setting -the “initial behavior” in the constructor by calling the :meth:`receive` method +The basic idea behind `Event Sourcing`_ is quite simple. A persistent actor receives a (non-persistent) command +which is first validated if it can be applied to the current state. Here, validation can mean anything, from simple +inspection of a command message's fields up to a conversation with several external services, for example. +If validation succeeds, events are generated from the command, representing the effect of the command. These events +are then persisted and, after successful persistence, used to change the actor's state. When the persistent actor +needs to be recovered, only the persisted events are replayed of which we know that they can be successfully applied. +In other words, events cannot fail when being replayed to a persistent actor, in contrast to commands. Event sourced +actors may of course also process commands that do not change application state, such as query commands, for example. -.. includecode:: ../../../akka-samples/akka-sample-persistence-java-lambda/src/main/java/doc/LambdaPersistenceDocTest.java#definition +.. _Event Sourcing: http://martinfowler.com/eaaDev/EventSourcing.html -Processors only write messages of type ``Persistent`` to the journal, others are received without being persisted. -When a processor's behavior 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 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. +Akka persistence supports event sourcing with the ``AbstractPersistentActor`` abstract class. An actor that extends this +class uses the ``persist`` method to persist and handle events. The behavior of an ``AbstractPersistentActor`` +is defined by implementing ``receiveRecover`` and ``receiveCommand``. This is demonstrated in the following example. -An ``AbstractProcessor`` itself is an ``Actor`` and can therefore be instantiated with ``actorOf``. +.. includecode:: ../../../akka-samples/akka-sample-persistence-java-lambda/src/main/java/sample/persistence/PersistentActorExample.java#persistent-actor-example -.. includecode:: ../../../akka-samples/akka-sample-persistence-java-lambda/src/main/java/doc/LambdaPersistenceDocTest.java#usage +The example defines two data types, ``Cmd`` and ``Evt`` to represent commands and events, respectively. The +``state`` of the ``ExampleProcessor`` is a list of persisted event data contained in ``ExampleState``. + +The persistent actor's ``receiveRecover`` method defines how ``state`` is updated during recovery by handling ``Evt`` +and ``SnapshotOffer`` messages. The persistent actor's ``receiveCommand`` method is a command handler. In this example, +a command is handled by generating two events which are then persisted and handled. Events are persisted by calling +``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 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. + +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`` +calls in context of a single command. + +The easiest way to run this example yourself is to download `Typesafe Activator `_ +and open the tutorial named `Akka Persistence Samples in Java with Lambdas `_. +It contains instructions on how to run the ``PersistentActorExample``. + +.. note:: + + It's also possible to switch between different command handlers during normal processing and recovery + with ``context().become()`` and ``context().unbecome()``. To get the actor into the same state after + recovery you need to take special care to perform the same state transitions with ``become`` and + ``unbecome`` in the ``receiveRecover`` method as you would have done in the command handler. + +Identifiers +----------- + +A persistent actor must have an identifier that doesn't change across different actor incarnations. It defaults to the +``String`` representation of persistent actor's path without the address part and can be obtained via the ``persistenceId`` +method. + +.. includecode:: ../../../akka-samples/akka-sample-persistence-java-lambda/src/main/java/doc/LambdaPersistenceDocTest.java#persistence-id + +Applications can customize a persistent actor's id by specifying an actor name during persistent actor creation as shown in +section :ref:`event-sourcing-java-lambda`. This changes that persistent actor's name in its actor hierarchy and hence influences only +part of the persistent actor id. To fully customize a persistent actor's id, the ``persistenceId`` method must be overridden. + +.. includecode:: ../../../akka-samples/akka-sample-persistence-java-lambda/src/main/java/doc/LambdaPersistenceDocTest.java#persistence-id-override + +Overriding ``persistenceId`` is the recommended way to generate stable identifiers. + +.. _recovery-java-lambda: Recovery -------- -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 a processor after recovery completes. +By default, a persistent actor is automatically recovered on start and on restart by replaying journaled messages. +New messages sent to a persistent actor during recovery do not interfere with replayed messages. New messages will +only be received by a persistent actor after recovery completes. Recovery customization ^^^^^^^^^^^^^^^^^^^^^^ @@ -106,7 +158,7 @@ Automated recovery on start can be disabled by overriding ``preStart`` with an e .. includecode:: ../../../akka-samples/akka-sample-persistence-java-lambda/src/main/java/doc/LambdaPersistenceDocTest.java#recover-on-start-disabled -In this case, a processor must be recovered explicitly by sending it a ``Recover`` message. +In this case, a persistent actor must be recovered explicitly by sending it a ``Recover`` message. .. includecode:: ../../../akka-samples/akka-sample-persistence-java-lambda/src/main/java/doc/LambdaPersistenceDocTest.java#recover-explicit @@ -115,7 +167,7 @@ If not overridden, ``preStart`` sends a ``Recover`` message to ``self()``. Appli .. includecode:: ../../../akka-samples/akka-sample-persistence-java-lambda/src/main/java/doc/LambdaPersistenceDocTest.java#recover-on-start-custom -Upper sequence number bounds can be used to recover a processor to past state instead of current state. Automated +Upper sequence number bounds can be used to recover a persistent actor to past state instead of current state. Automated recovery on restart can be disabled by overriding ``preRestart`` with an empty implementation. .. includecode:: ../../../akka-samples/akka-sample-persistence-java-lambda/src/main/java/doc/LambdaPersistenceDocTest.java#recover-on-restart-disabled @@ -123,56 +175,90 @@ recovery on restart can be disabled by overriding ``preRestart`` with an empty i Recovery status ^^^^^^^^^^^^^^^ -A processor can query its own recovery status via the methods +A persistent actor can query its own recovery status via the methods .. includecode:: ../../../akka-samples/akka-sample-persistence-java-lambda/src/main/java/doc/LambdaPersistenceDocTest.java#recovery-status Sometimes there is a need for performing additional initialization when the -recovery has completed, before processing any other message sent to the processor. -The processor will receive a special :class:`RecoveryCompleted` message right after recovery -and before any other received messages. If there is a problem with recovering the state of -the actor from the journal, the actor will be sent a :class:`RecoveryFailure` message that -it can choose to handle. If the actor doesn't handle the :class:`RecoveryFailure` message it -will be stopped. +recovery has completed, before processing any other message sent to the persistent actor. +The persistent actor will receive a special :class:`RecoveryCompleted` message right after recovery +and before any other received messages. + +If there is a problem with recovering the state of the actor from the journal, the actor will be +sent a :class:`RecoveryFailure` message that it can choose to handle in ``receiveRecover``. If the +actor doesn't handle the :class:`RecoveryFailure` message it will be stopped. .. includecode:: ../../../akka-samples/akka-sample-persistence-java-lambda/src/main/java/doc/LambdaPersistenceDocTest.java#recovery-completed -.. _failure-handling-java-lambda: +Relaxed local consistency requirements and high throughput use-cases +-------------------------------------------------------------------- -Failure handling -^^^^^^^^^^^^^^^^ +If faced with relaxed local consistency requirements and high throughput demands sometimes ``PersistentActor`` and it's +``persist`` may not be enough in terms of consuming incoming Commands at a high rate, because it has to wait until all +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 +want to process commands as fast as you can, assuming that Event will eventually be persisted and handled properly in +the background and retroactively reacting to persistence failures if needed. -A persistent message that caused an exception will be received again by a processor after restart. To prevent -a replay of that message during recovery it can be deleted. +The ``persistAsync`` method provides a tool for implementing high-throughput persistent actors. It will *not* +stash incoming Commands while the Journal is still working on persisting and/or user code is executing event callbacks. -.. includecode:: ../../../akka-samples/akka-sample-persistence-java-lambda/src/main/java/doc/LambdaPersistenceDocTest.java#deletion +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.). + +.. includecode:: ../../../akka-samples/akka-sample-persistence-java-lambda/src/main/java/doc/LambdaPersistenceDocTest.java#persist-async + +.. note:: + In order to implement the pattern known as "*command sourcing*" simply ``persistAsync`` all incoming events right away, + and handle them in the callback. + +.. _defer-java-lambda: + +Deferring actions until preceeding persist handlers have executed +----------------------------------------------------------------- + +Sometimes when working with ``persistAsync`` you may find that it would be nice to define some actions in terms of +''happens-after the previous ``persistAsync`` handlers have been invoked''. ``PersistentActor`` provides an utility method +called ``defer``, which works similarily to ``persistAsync`` yet does not persist the passed in event. It is recommended to +use it for *read* operations, and actions which do not have corresponding events in your domain model. + +Using this method is very similar to the persist family of methods, yet it does **not** persist the passed in event. +It will be kept in memory and used when invoking the handler. + +.. includecode:: ../../../akka-samples/akka-sample-persistence-java-lambda/src/main/java/doc/LambdaPersistenceDocTest.java#defer + +Notice that the ``sender()`` is **safe** to access in the handler callback, and will be pointing to the original sender +of the command for which this ``defer`` handler was called. + +.. includecode:: ../../../akka-samples/akka-sample-persistence-java-lambda/src/main/java/doc/LambdaPersistenceDocTest.java#defer-caller + +Batch writes +------------ + +To optimize throughput, a persistent actor internally batches events to be stored under high load before +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. When using ``persistAsync`` this increases +the maximum throughput dramatically. + +.. includecode:: ../scala/code/docs/persistence/PersistencePluginDocSpec.scala#max-message-batch-size + +A new batch write is triggered by a persistent actor 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 at a minimum. + +The batches are also used internally to ensure atomic 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 ``AbstractPersistentActor`` will therefore never be done partially (with only a subset of events persisted by a +single command). Message deletion ---------------- -A processor can delete a single message by calling the ``deleteMessage`` method with the sequence number of +A persistent actor can delete a single message by calling the ``deleteMessage`` method with the sequence number of that message as argument. An optional ``permanent`` parameter specifies whether the message shall be permanently 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 should call the ``deleteMessages`` method. - -Identifiers ------------ - -A processor must have an identifier that doesn't change across different actor incarnations. It defaults to the -``String`` representation of processor's path without the address part and can be obtained via the ``persistenceId`` -method. - -.. includecode:: ../../../akka-samples/akka-sample-persistence-java-lambda/src/main/java/doc/LambdaPersistenceDocTest.java#persistence-id - -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 ``persistenceId`` method must be overridden. - -.. includecode:: ../../../akka-samples/akka-sample-persistence-java-lambda/src/main/java/doc/LambdaPersistenceDocTest.java#persistence-id-override - -Overriding ``persistenceId`` is the recommended way to generate stable identifiers. +purposes, for example. To delete all messages (journaled by a single persistent actor) up to a specified sequence number, +persistent actors should call the ``deleteMessages`` method. .. _views-java-lambda: @@ -184,9 +270,9 @@ and setting the “initial behavior” in the constructor by calling the :meth:` .. includecode:: ../../../akka-samples/akka-sample-persistence-java-lambda/src/main/java/doc/LambdaPersistenceDocTest.java#view -The ``persistenceId`` 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 +The ``persistenceId`` identifies the persistent actor from which the view receives journaled messages. It is not necessary +the referenced persistent actor is actually running. Views read messages from a persistent actor's journal directly. When a +persistent actor is started later and begins to write new messages, the corresponding view is updated automatically, by default. Updates @@ -219,9 +305,11 @@ of replayed messages for manual updates can be limited with the ``replayMax`` pa Recovery -------- -Initial recovery of views works in the very same way as for :ref:`processors` (i.e. by sending a ``Recover`` message +Initial recovery of views works in the very same way as for a persistent actor (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`. +Further possibilities to customize initial recovery are explained in section :ref:`recovery-java-lambda`. + +.. _persistence-identifiers-java-lambda: Identifiers ----------- @@ -234,184 +322,38 @@ Applications can customize a view's id by specifying an actor name during view c 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 ``persistenceId``, unless :ref:`snapshots-java` of a view and its -processor shall be shared (which is what applications usually do not want). +The ``viewId`` must differ from the referenced ``persistenceId``, unless :ref:`snapshots-java-lambda` of a view and its +persistent actor shall be shared (which is what applications usually do not want). -.. _channels-java-lambda: +.. _streams-java-lambda: -Channels -======== +Streams +======= -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. - -Channels prevent redundant delivery of replayed messages to destinations during processor recovery. A replayed -message is retained by a channel if its delivery has been confirmed by a destination. - -.. includecode:: ../../../akka-samples/akka-sample-persistence-java-lambda/src/main/java/doc/LambdaPersistenceDocTest.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. 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 ``sender()`` path as -channel destination. - -.. includecode:: ../../../akka-samples/akka-sample-persistence-java-lambda/src/main/java/doc/LambdaPersistenceDocTest.java#channel-example-reply - -Persistent messages delivered by a channel are of type ``ConfirmablePersistent``. ``ConfirmablePersistent`` extends -``Persistent`` by adding the methods ``confirm`` and ``redeliveries`` (see also :ref:`redelivery-java-lambda`). 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. - -.. _redelivery-java-lambda: - -Message re-delivery -------------------- - -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. The number of re-delivery -attempts can be obtained via the ``redeliveries`` method on ``ConfirmablePersistent``. - -.. includecode:: ../../../akka-samples/akka-sample-persistence-java-lambda/src/main/java/doc/LambdaPersistenceDocTest.java#channel-custom-settings - -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. - -.. includecode:: ../../../akka-samples/akka-sample-persistence-java-lambda/src/main/java/doc/LambdaPersistenceDocTest.java#channel-custom-listener - -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 - -* message persistence by sending processors -* message replays by sending processors -* message re-deliveries by channels and -* application-level confirmations (acknowledgements) by destinations - -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. - -.. warning:: - - 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 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. - -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 -------------------- - -Channels created with ``Channel.props`` do not persist messages. These channels are usually used in combination -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. - -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:: ../../../akka-samples/akka-sample-persistence-java-lambda/src/main/java/doc/LambdaPersistenceDocTest.java#persistent-channel-example - -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``. - -.. includecode:: ../../../akka-samples/akka-sample-persistence-java-lambda/src/main/java/doc/LambdaPersistenceDocTest.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:: ../../../akka-samples/akka-sample-persistence-java-lambda/src/main/java/doc/LambdaPersistenceDocTest.java#persistent-channel-reply - -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-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:: ../../../akka-samples/akka-sample-persistence-java-lambda/src/main/java/doc/LambdaPersistenceDocTest.java#channel-id-override - -Persistent messages -=================== - -Payload -------- - -The payload of a ``Persistent`` message can be obtained via its ``payload`` method. Inside processors, new messages -must be derived from the current persistent message before sending them via a channel, either by calling ``p.withPayload(...)`` -or ``Persistent.create(..., getCurrentPersistentMessage())`` where ``getCurrentPersistentMessage()`` is defined on -``AbstractProcessor``. - -.. includecode:: ../../../akka-samples/akka-sample-persistence-java-lambda/src/main/java/doc/LambdaPersistenceDocTest.java#current-message - -This is necessary for delivery confirmations to work properly. Both -ways are equivalent but we recommend using ``p.withPayload(...)`` for clarity. It is not allowed to send a message -via a channel that has been created with ``Persistent.create(...)``. This would redeliver the message on every replay -even though its delivery was confirmed by a destination. - -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 messages. +Java API coming soon. See also Scala :ref:`streams` documentation. .. _snapshots-java-lambda: Snapshots ========= -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. +Snapshots can dramatically reduce recovery times of persistent actors and views. The following discusses snapshots +in context of persistent actors 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 +Persistent actor 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 .. includecode:: ../../../akka-samples/akka-sample-persistence-java-lambda/src/main/java/doc/LambdaPersistenceDocTest.java#save-snapshot -During recovery, the processor is offered a previously saved snapshot via a ``SnapshotOffer`` message from +During recovery, the persistent actor is offered a previously saved snapshot via a ``SnapshotOffer`` message from which it can initialize internal state. .. includecode:: ../../../akka-samples/akka-sample-persistence-java-lambda/src/main/java/doc/LambdaPersistenceDocTest.java#snapshot-offer The replayed messages that follow the ``SnapshotOffer`` message, if any, are younger than the offered snapshot. -They finally recover the processor to its current (i.e. latest) state. +They finally recover the persistent actor to its current (i.e. latest) state. -In general, a processor is only offered a snapshot if that processor has previously saved one or more snapshots +In general, a persistent actor is only offered a snapshot if that persistent actor has previously saved one or more snapshots and at least one of these snapshots matches the ``SnapshotSelectionCriteria`` that can be specified for recovery. .. includecode:: ../../../akka-samples/akka-sample-persistence-java-lambda/src/main/java/doc/LambdaPersistenceDocTest.java#snapshot-criteria @@ -423,165 +365,10 @@ saved snapshot matches the specified ``SnapshotSelectionCriteria`` will replay a Snapshot deletion ----------------- -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 +A persistent actor 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``, persistent actors should use the ``deleteSnapshots`` method. -.. _event-sourcing-java-lambda: - -Event sourcing -============== - -.. note:: - The ``PersistentActor`` introduced in this section was previously known as ``EventsourcedProcessor``, - which was a subset of the ``PersistentActor``. Migrating your code to use persistent actors instead is - very simple and is explained in the :ref:`migration-guide-persistence-experimental-2.3.x-2.4.x`. - -In all the examples so far, messages that change a processor's state have been sent as ``Persistent`` messages -by an application, so that they can be replayed during recovery. From this point of view, the journal acts as -a write-ahead-log for whatever ``Persistent`` messages a processor receives. This is also known as *command -sourcing*. Commands, however, may fail and some applications cannot tolerate command failures during recovery. - -For these applications `Event Sourcing`_ is a better choice. Applied to Akka persistence, the basic idea behind -event sourcing is quite simple. A processor receives a (non-persistent) command which is first validated if it -can be applied to the current state. Here, validation can mean anything, from simple inspection of a command -message's fields up to a conversation with several external services, for example. If validation succeeds, events -are generated from the command, representing the effect of the command. These events are then persisted and, after -successful persistence, used to change a processor's state. When the processor needs to be recovered, only the -persisted events are replayed of which we know that they can be successfully applied. In other words, events -cannot fail when being replayed to a processor, in contrast to commands. Eventsourced processors may of course -also process commands that do not change application state, such as query commands, for example. - -.. _Event Sourcing: http://martinfowler.com/eaaDev/EventSourcing.html - -Akka persistence supports event sourcing with the ``AbstractPersistentActor`` abstract class (which implements -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 -``AbstractEventsPersistentActordefined by implementing ``receiveRecover`` and ``receiveCommand``. This is -demonstrated in the following example. - -.. includecode:: ../../../akka-samples/akka-sample-persistence-java-lambda/src/main/java/sample/persistence/PersistentActorExample.java#persistent-actor-example - -The example defines two data types, ``Cmd`` and ``Evt`` to represent commands and events, respectively. The -``state`` of the ``ExampleProcessor`` is a list of persisted event data contained in ``ExampleState``. - -The processor's ``receiveRecover`` method defines how ``state`` is updated during recovery by handling ``Evt`` -and ``SnapshotOffer`` messages. The processor's ``receiveCommand`` method is a command handler. In this example, -a command is handled by generating two events which are then persisted and handled. Events are persisted by calling -``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 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 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 easiest way to run this example yourself is to download `Typesafe Activator `_ -and open the tutorial named `Akka Persistence Samples in Java with Lambdas `_. -It contains instructions on how to run the ``PersistentActorExample``. - -.. note:: - - It's also possible to switch between different command handlers during normal processing and recovery - with ``context().become()`` and ``context().unbecome()``. To get the actor into the same state after - recovery you need to take special care to perform the same state transitions with ``become`` and - ``unbecome`` in the ``receiveRecover`` method as you would have done in the command handler. - -Relaxed local consistency requirements and high throughput use-cases --------------------------------------------------------------------- - -If faced with Relaxed local consistency requirements and high throughput demands sometimes ``PersistentActor`` and it's -``persist`` may not be enough in terms of consuming incoming Commands at a high rate, because it has to wait until all -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 -want to process commands as fast as you can, assuming that Event will eventually be persisted and handled properly in -the background and retroactively reacting to persistence failures if needed. - -The ``persistAsync`` method provides a tool for implementing high-throughput processors. It will *not* -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.). - -.. includecode:: ../../../akka-samples/akka-sample-persistence-java-lambda/src/main/java/doc/LambdaPersistenceDocTest.java#persist-async - -Notice that the client does not have to wrap any messages in the `Persistent` class in order to obtain "command sourcing like" -semantics. It's up to the processor to decide about persisting (or not) of messages, unlike ``Processor`` where the sender had to be aware of this decision. - -.. note:: - In order to implement the pattern known as "*command sourcing*" simply ``persistAsync`` all incoming events right away, - and handle them in the callback. - -.. _defer-java-lambda: - -Deferring actions until preceeding persist handlers have executed ------------------------------------------------------------------ - -Sometimes when working with ``persistAsync`` you may find that it would be nice to define some actions in terms of -''happens-after the previous ``persistAsync`` handlers have been invoked''. ``PersistentActor`` provides an utility method -called ``defer``, which works similarily to ``persistAsync`` yet does not persist the passed in event. It is recommended to -use it for *read* operations, and actions which do not have corresponding events in your domain model. - -Using this method is very similar to the persist family of methods, yet it does **not** persist the passed in event. -It will be kept in memory and used when invoking the handler. - -.. includecode:: ../../../akka-samples/akka-sample-persistence-java-lambda/src/main/java/doc/LambdaPersistenceDocTest.java#defer - -Notice that the ``sender()`` is **safe** to access in the handler callback, and will be pointing to the original sender -of the command for which this ``defer`` handler was called. - -.. includecode:: ../../../akka-samples/akka-sample-persistence-java-lambda/src/main/java/doc/LambdaPersistenceDocTest.java#defer-caller - -Reliable event delivery ------------------------ - -Sending events from an event handler to another actor has at-most-once delivery semantics. For at-least-once delivery, -:ref:`channels-java-lambda` must be used. In this case, also replayed events (received by ``receiveRecover``) must be -sent to a channel, as shown in the following example: - -.. includecode:: ../../../akka-samples/akka-sample-persistence-java-lambda/src/main/java/doc/LambdaPersistenceDocTest.java#reliable-event-delivery - -In larger integration scenarios, channel destinations may be actors that submit received events to an external -message broker, for example. After having successfully submitted an event, they should call ``confirm()`` on the -received ``ConfirmablePersistent`` message. - -Batch writes -============ - -To optimize throughput, an ``AbstractProcessor`` internally batches received ``Persistent`` messages under high load -before -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-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 at a minimum. - -Applications that want to have more explicit control over batch writes and batch sizes can send processors -``PersistentBatch`` messages. - -.. includecode:: ../../../akka-samples/akka-sample-persistence-java-lambda/src/main/java/doc/LambdaPersistenceDocTest.java#batch-write - -``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 ``AbstractEventsourcedPersistentActor atomic -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 ``AbstractPersistentActor`` -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-lambda` 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 =============== @@ -656,7 +443,7 @@ 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 and continue using the shared journal instance from the +example, allows persistent actors to failover to a backup node and continue using the shared journal instance from the backup node. .. warning:: @@ -683,7 +470,7 @@ done by calling the ``SharedLeveldbJournal.setStore`` method with the actor refe .. includecode:: ../../../akka-samples/akka-sample-persistence-java-lambda/src/main/java/doc/LambdaPersistencePluginDocTest.java#shared-store-usage -Internal journal commands (sent by processors) are buffered until injection completes. Injection is idempotent +Internal journal commands (sent by persistent actors) are buffered until injection completes. Injection is idempotent i.e. only the first injection is used. .. _local-snapshot-store-java-lambda: diff --git a/akka-docs/rst/java/persistence.rst b/akka-docs/rst/java/persistence.rst index 88d946fac1..0ad339252a 100644 --- a/akka-docs/rst/java/persistence.rst +++ b/akka-docs/rst/java/persistence.rst @@ -5,9 +5,6 @@ Persistence ########### -Java 8 lambda expressions are also supported now. (See section :ref:`persistence-lambda-java`) - - Akka persistence enables stateful actors to persist their internal state so that it can be recovered when an actor 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 @@ -15,11 +12,11 @@ persistence is that only changes to an actor's internal state are persisted but 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. +communication with at-least-once message delivery semantics. -.. Lambda warning:: +.. note:: - Java 8 lambda expressions are also supported now. (See section :ref:`persistence-lambda-java`) + Java 8 lambda expressions are also supported. (See section :ref:`persistence-lambda-java`) .. warning:: @@ -48,75 +45,113 @@ Akka persistence is a separate jar file. Make sure that you have the following d Architecture ============ -* *Processor* (deprecated, use *PersistentActor* instead): A processor is a persistent, stateful actor. Messages sent - to a processor are written to a journal 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. - -* *PersistentActor*: Is a persistent, stateful actor. It is able to persist events to a journal and can react to +* *UntypedPersistentActor*: Is a persistent, stateful actor. It is able to persist events to a journal and can react to 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 recover internal state from these messages. * *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 + persistent actor. A view itself does not journal new messages, instead, it updates internal state only from a persistent actor's replicated message stream. -* *Streams*: Messages written by a processor can be published in compliance with the `Reactive Streams`_ specification. - Only those messages that are explicitly requested from downstream processors are actually pulled from a processor's +* *Streams*: Messages written by a persistent actor can be published in compliance with the `Reactive Streams`_ specification. + Only those messages that are explicitly requested from downstream persistent actors are actually pulled from a persistent actor's journal. -* *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 +* *UntypedPersistentActorAtLeastOnceDelivery*: To send messages with at-least-once delivery semantics to destinations, 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 journaled and which are received by the processor without being journaled. The storage backend of a journal is +* *Journal*: 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. The storage backend of a journal is pluggable. The default journal storage plugin writes to the local filesystem, replicated journals are available as `Community plugins`_. -* *Snapshot store*: A snapshot store persists snapshots of a processor's or a view's internal state. Snapshots are +* *Snapshot store*: A snapshot store persists snapshots of a persistent actor'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`) - .. _Community plugins: http://akka.io/community/ .. _Reactive Streams: http://www.reactive-streams.org/ -.. _processors-java: +.. _event-sourcing-java: -Processors -========== +Event sourcing +============== -.. warning:: - ``Processor`` is deprecated. Instead the current ``PersistentActor`` will be extended to provide equivalent - functionality if required (by introducing the ``persistAsync`` method). - For details see `Relaxed local consistency requirements and high throughput use-cases`_ as well as the discussion - and pull requests related to this `issue on Github `_. +The basic idea behind `Event Sourcing`_ is quite simple. A persistent actor receives a (non-persistent) command +which is first validated if it can be applied to the current state. Here, validation can mean anything, from simple +inspection of a command message's fields up to a conversation with several external services, for example. +If validation succeeds, events are generated from the command, representing the effect of the command. These events +are then persisted and, after successful persistence, used to change the actor's state. When the persistent actor +needs to be recovered, only the persisted events are replayed of which we know that they can be successfully applied. +In other words, events cannot fail when being replayed to a persistent actor, in contrast to commands. Event sourced +actors may of course also process commands that do not change application state, such as query commands, for example. -A processor can be implemented by extending the abstract ``UntypedProcessor`` class and implementing the -``onReceive`` method. +.. _Event Sourcing: http://martinfowler.com/eaaDev/EventSourcing.html -.. includecode:: code/docs/persistence/PersistenceDocTest.java#definition +Akka persistence supports event sourcing with the ``UntypedPersistentActor`` abstract class. An actor that extends this +class uses the ``persist`` method to persist and handle events. The behavior of an ``UntypedPersistentActor`` +is defined by implementing ``receiveRecover`` and ``receiveCommand``. This is demonstrated in the following example. -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 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. +.. includecode:: ../../../akka-samples/akka-sample-persistence-java/src/main/java/sample/persistence/PersistentActorExample.java#persistent-actor-example -An ``UntypedProcessor`` itself is an ``Actor`` and can therefore be instantiated with ``actorOf``. +The example defines two data types, ``Cmd`` and ``Evt`` to represent commands and events, respectively. The +``state`` of the ``ExampleProcessor`` is a list of persisted event data contained in ``ExampleState``. -.. includecode:: code/docs/persistence/PersistenceDocTest.java#usage +The persistent actor's ``onReceiveRecover`` method defines how ``state`` is updated during recovery by handling ``Evt`` +and ``SnapshotOffer`` messages. The persistent actor's ``onReceiveCommand`` method is a command handler. In this example, +a command is handled by generating two events which are then persisted and handled. Events are persisted by calling +``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 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. + +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`` +calls in context of a single command. + +The easiest way to run this example yourself is to download `Typesafe Activator `_ +and open the tutorial named `Akka Persistence Samples with Java `_. +It contains instructions on how to run the ``PersistentActorExample``. + +.. note:: + + It's also possible to switch between different command handlers during normal processing and recovery + with ``getContext().become()`` and ``getContext().unbecome()``. To get the actor into the same state after + recovery you need to take special care to perform the same state transitions with ``become`` and + ``unbecome`` in the ``receiveRecover`` method as you would have done in the command handler. + +Identifiers +----------- + +A persistent actor must have an identifier that doesn't change across different actor incarnations. It defaults to the +``String`` representation of persistent actor's path without the address part and can be obtained via the ``persistenceId`` +method. + +.. includecode:: code/docs/persistence/PersistenceDocTest.java#persistence-id + +Applications can customize a persistent actor's id by specifying an actor name during persistent actor creation as shown in +section :ref:`event-sourcing-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 ``persistenceId`` method must be overridden. + +.. includecode:: code/docs/persistence/PersistenceDocTest.java#persistence-id-override + +Overriding ``persistenceId`` is the recommended way to generate stable identifiers. + +.. _recovery-java: Recovery -------- -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 a processor after recovery completes. +By default, a persistent actor is automatically recovered on start and on restart by replaying journaled messages. +New messages sent to a persistent actor during recovery do not interfere with replayed messages. New messages will +only be received by a persistent actor after recovery completes. Recovery customization ^^^^^^^^^^^^^^^^^^^^^^ @@ -125,7 +160,7 @@ Automated recovery on start can be disabled by overriding ``preStart`` with an e .. includecode:: code/docs/persistence/PersistenceDocTest.java#recover-on-start-disabled -In this case, a processor must be recovered explicitly by sending it a ``Recover`` message. +In this case, a persistent actor must be recovered explicitly by sending it a ``Recover`` message. .. includecode:: code/docs/persistence/PersistenceDocTest.java#recover-explicit @@ -134,7 +169,7 @@ If not overridden, ``preStart`` sends a ``Recover`` message to ``getSelf()``. Ap .. includecode:: code/docs/persistence/PersistenceDocTest.java#recover-on-start-custom -Upper sequence number bounds can be used to recover a processor to past state instead of current state. Automated +Upper sequence number bounds can be used to recover a persistent actor to past state instead of current state. Automated recovery on restart can be disabled by overriding ``preRestart`` with an empty implementation. .. includecode:: code/docs/persistence/PersistenceDocTest.java#recover-on-restart-disabled @@ -142,56 +177,93 @@ recovery on restart can be disabled by overriding ``preRestart`` with an empty i Recovery status ^^^^^^^^^^^^^^^ -A processor can query its own recovery status via the methods +A persistent actor can query its own recovery status via the methods .. includecode:: code/docs/persistence/PersistenceDocTest.java#recovery-status Sometimes there is a need for performing additional initialization when the -recovery has completed, before processing any other message sent to the processor. -The processor will receive a special :class:`RecoveryCompleted` message right after recovery -and before any other received messages. If there is a problem with recovering the state of -the actor from the journal, the actor will be sent a :class:`RecoveryFailure` message that -it can choose to handle. If the actor doesn't handle the :class:`RecoveryFailure` message it -will be stopped. +recovery has completed, before processing any other message sent to the persistent actor. +The persistent actor will receive a special :class:`RecoveryCompleted` message right after recovery +and before any other received messages. + +If there is a problem with recovering the state of the actor from the journal, the actor will be +sent a :class:`RecoveryFailure` message that it can choose to handle in ``receiveRecover``. If the +actor doesn't handle the :class:`RecoveryFailure` message it will be stopped. .. includecode:: code/docs/persistence/PersistenceDocTest.java#recovery-completed -.. _failure-handling-java: +.. _persist-async-java: -Failure handling -^^^^^^^^^^^^^^^^ +Relaxed local consistency requirements and high throughput use-cases +-------------------------------------------------------------------- -A persistent message that caused an exception will be received again by a processor after restart. To prevent -a replay of that message during recovery it can be deleted. +If faced with relaxed local consistency requirements and high throughput demands sometimes ``PersistentActor`` and it's +``persist`` may not be enough in terms of consuming incoming Commands at a high rate, because it has to wait until all +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 +want to process commands as fast as you can, assuming that Event will eventually be persisted and handled properly in +the background and retroactively reacting to persistence failures if needed. + +The ``persistAsync`` method provides a tool for implementing high-throughput persistent actors. It will *not* +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.). + +.. includecode:: code/docs/persistence/PersistenceDocTest.java#persist-async + +.. note:: + In order to implement the pattern known as "*command sourcing*" simply ``persistAsync`` all incoming events right away, + and handle them in the callback. + +.. _defer-java: + +Deferring actions until preceeding persist handlers have executed +----------------------------------------------------------------- + +Sometimes when working with ``persistAsync`` you may find that it would be nice to define some actions in terms of +''happens-after the previous ``persistAsync`` handlers have been invoked''. ``PersistentActor`` provides an utility method +called ``defer``, which works similarily to ``persistAsync`` yet does not persist the passed in event. It is recommended to +use it for *read* operations, and actions which do not have corresponding events in your domain model. + +Using this method is very similar to the persist family of methods, yet it does **not** persist the passed in event. +It will be kept in memory and used when invoking the handler. + +.. includecode:: code/docs/persistence/PersistenceDocTest.java#defer + +Notice that the ``sender()`` is **safe** to access in the handler callback, and will be pointing to the original sender +of the command for which this ``defer`` handler was called. + +.. includecode:: code/docs/persistence/PersistenceDocTest.java#defer-caller + +Batch writes +------------ + +To optimize throughput, a persistent actor internally batches events to be stored under high load before +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. When using ``persistAsync`` this increases +the maximum throughput dramatically. + +.. includecode:: ../scala/code/docs/persistence/PersistencePluginDocSpec.scala#max-message-batch-size + +A new batch write is triggered by a persistent actor 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 at a minimum. + +The batches are also used internally to ensure atomic 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 ``UntypedPersistentActor`` will therefore never be done partially (with only a subset of events persisted by a +single command). -.. includecode:: code/docs/persistence/PersistenceDocTest.java#deletion Message deletion ---------------- -A processor can delete a single message by calling the ``deleteMessage`` method with the sequence number of +A persistent actor can delete a single message by calling the ``deleteMessage`` method with the sequence number of that message as argument. An optional ``permanent`` parameter specifies whether the message shall be permanently 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 should call the ``deleteMessages`` method. - -Identifiers ------------ - -A processor must have an identifier that doesn't change across different actor incarnations. It defaults to the -``String`` representation of processor's path without the address part and can be obtained via the ``persistenceId`` -method. - -.. includecode:: code/docs/persistence/PersistenceDocTest.java#persistence-id - -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 ``persistenceId`` method must be overridden. - -.. includecode:: code/docs/persistence/PersistenceDocTest.java#persistence-id-override - -Overriding ``persistenceId`` is the recommended way to generate stable identifiers. +purposes, for example. To delete all messages (journaled by a single persistent actor) up to a specified sequence number, +persistent actors should call the ``deleteMessages`` method. .. _views-java: @@ -203,9 +275,9 @@ methods. .. includecode:: code/docs/persistence/PersistenceDocTest.java#view -The ``persistenceId`` 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 +The ``persistenceId`` identifies the persistent actor from which the view receives journaled messages. It is not necessary +the referenced persistent actor is actually running. Views read messages from a persistent actor's journal directly. When a +persistent actor is started later and begins to write new messages, the corresponding view is updated automatically, by default. Updates @@ -238,9 +310,11 @@ of replayed messages for manual updates can be limited with the ``replayMax`` pa Recovery -------- -Initial recovery of views works in the very same way as for :ref:`processors` (i.e. by sending a ``Recover`` message +Initial recovery of views works in the very same way as for a persistent actor (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`. +Further possibilities to customize initial recovery are explained in section :ref:`recovery-java`. + +.. _persistence-identifiers-java: Identifiers ----------- @@ -254,7 +328,7 @@ name in its actor hierarchy and hence influences only part of the view id. To fu ``viewId`` method must be overridden. Overriding ``viewId`` is the recommended way to generate stable identifiers. The ``viewId`` must differ from the referenced ``persistenceId``, unless :ref:`snapshots-java` of a view and its -processor shall be shared (which is what applications usually do not want). +persistent actor shall be shared (which is what applications usually do not want). .. _streams-java: @@ -263,199 +337,28 @@ Streams Java API coming soon. See also Scala :ref:`streams` documentation. -.. _channels-java: - -Channels -======== - -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. - -Channels prevent redundant delivery of replayed messages to destinations during processor recovery. A replayed -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. 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. - -.. note:: - - Sending via a channel has at-least-once delivery semantics—by virtue of either - the sending actor or the channel being persistent—which means that the - semantics do not match those of a normal :class:`ActorRef` send operation: - - * it is not at-most-once delivery - - * message order for the same sender–receiver pair is not retained due to - possible resends - - * after a crash and restart of the destination messages are still - delivered—to the new actor incarnation - - These semantics match precisely what an :class:`ActorPath` represents (see - :ref:`actor-lifecycle-java`), therefore you need to supply a path and not a - reference when constructing :class:`Deliver` messages. - -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`` 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. - -.. _redelivery-java: - -Message re-delivery -------------------- - -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. The number of re-delivery -attempts can be obtained via the ``redeliveries`` method on ``ConfirmablePersistent``. - -.. includecode:: code/docs/persistence/PersistenceDocTest.java#channel-custom-settings - -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. - -.. includecode:: code/docs/persistence/PersistenceDocTest.java#channel-custom-listener - -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 - -* message persistence by sending processors -* message replays by sending processors -* message re-deliveries by channels and -* application-level confirmations (acknowledgements) by destinations - -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. - -.. warning:: - - 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 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. - -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 -------------------- - -Channels created with ``Channel.props`` do not persist messages. These channels are usually used in combination -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. - -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 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``. - -.. 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`` -message. In case the latter case, the sender should re-send the message. - -Identifiers ------------ - -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 - -Persistent messages -=================== - -Payload -------- - -The payload of a ``Persistent`` message can be obtained via its ``payload`` method. Inside processors, new messages -must be derived from the current persistent message before sending them via a channel, either by calling ``p.withPayload(...)`` -or ``Persistent.create(..., getCurrentPersistentMessage())`` where ``getCurrentPersistentMessage()`` is defined on -``UntypedProcessor``. - -.. includecode:: code/docs/persistence/PersistenceDocTest.java#current-message - -This is necessary for delivery confirmations to work properly. Both -ways are equivalent but we recommend using ``p.withPayload(...)`` for clarity. It is not allowed to send a message -via a channel that has been created with ``Persistent.create(...)``. This would redeliver the message on every replay -even though its delivery was confirmed by a destination. - -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 messages. - .. _snapshots-java: Snapshots ========= -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. +Snapshots can dramatically reduce recovery times of persistent actor and views. The following discusses snapshots +in context of persistent actor 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 +Persistent actor 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 .. includecode:: code/docs/persistence/PersistenceDocTest.java#save-snapshot -During recovery, the processor is offered a previously saved snapshot via a ``SnapshotOffer`` message from +During recovery, the persistent actor is offered a previously saved snapshot via a ``SnapshotOffer`` message from which it can initialize internal state. .. includecode:: code/docs/persistence/PersistenceDocTest.java#snapshot-offer The replayed messages that follow the ``SnapshotOffer`` message, if any, are younger than the offered snapshot. -They finally recover the processor to its current (i.e. latest) state. +They finally recover the persistent actor to its current (i.e. latest) state. -In general, a processor is only offered a snapshot if that processor has previously saved one or more snapshots +In general, a persistent actor is only offered a snapshot if that persistent actor has previously saved one or more snapshots and at least one of these snapshots matches the ``SnapshotSelectionCriteria`` that can be specified for recovery. .. includecode:: code/docs/persistence/PersistenceDocTest.java#snapshot-criteria @@ -467,168 +370,10 @@ saved snapshot matches the specified ``SnapshotSelectionCriteria`` will replay a Snapshot deletion ----------------- -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 +A persistent actor 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``, persistent actors should use the ``deleteSnapshots`` method. -.. _event-sourcing-java: - -Event sourcing -============== - -.. note:: - The ``PersistentActor`` introduced in this section was previously known as ``EventsourcedProcessor``, - which was a subset of the ``PersistentActor``. Migrating your code to use persistent actors instead is - very simple and is explained in the :ref:`migration-guide-persistence-experimental-2.3.x-2.4.x`. - -In all the examples so far, messages that change a processor's state have been sent as ``Persistent`` messages -by an application, so that they can be replayed during recovery. From this point of view, the journal acts as -a write-ahead-log for whatever ``Persistent`` messages a processor receives. This is also known as *command -sourcing*. Commands, however, may fail and some applications cannot tolerate command failures during recovery. - -For these applications `Event Sourcing`_ is a better choice. Applied to Akka persistence, the basic idea behind -event sourcing is quite simple. A processor receives a (non-persistent) command which is first validated if it -can be applied to the current state. Here, validation can mean anything, from simple inspection of a command -message's fields up to a conversation with several external services, for example. If validation succeeds, events -are generated from the command, representing the effect of the command. These events are then persisted and, after -successful persistence, used to change a processor's state. When the processor needs to be recovered, only the -persisted events are replayed of which we know that they can be successfully applied. In other words, events -cannot fail when being replayed to a processor, in contrast to commands. Eventsourced processors may of course -also process commands that do not change application state, such as query commands, for example. - -.. _Event Sourcing: http://martinfowler.com/eaaDev/EventSourcing.html - -Akka persistence supports event sourcing with the abstract ``UntypedPersistentActor`` class (which implements -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 -``UntypedPersistentActor`` is defined by implementing ``onReceiveRecover`` and ``onReceiveCommand``. This is -demonstrated in the following example. - -.. includecode:: ../../../akka-samples/akka-sample-persistence-java/src/main/java/sample/persistence/PersistentActorExample.java#persistent-actor-example - -The example defines two data types, ``Cmd`` and ``Evt`` to represent commands and events, respectively. The -``state`` of the ``ExampleProcessor`` is a list of persisted event data contained in ``ExampleState``. - -The processor's ``onReceiveRecover`` method defines how ``state`` is updated during recovery by handling ``Evt`` -and ``SnapshotOffer`` messages. The processor's ``onReceiveCommand`` method is a command handler. In this example, -a command is handled by generating two events which are then persisted and handled. Events are persisted by calling -``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 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 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 easiest way to run this example yourself is to download `Typesafe Activator `_ -and open the tutorial named `Akka Persistence Samples with Java `_. -It contains instructions on how to run the ``PersistentActorExample``. - -.. note:: - - It's also possible to switch between different command handlers during normal processing and recovery - with ``getContext().become()`` and ``getContext().unbecome()``. To get the actor into the same state after - recovery you need to take special care to perform the same state transitions with ``become`` and - ``unbecome`` in the ``receiveRecover`` method as you would have done in the command handler. - -.. _persist-async-java: - -Relaxed local consistency requirements and high throughput use-cases --------------------------------------------------------------------- - -If faced with Relaxed local consistency requirements and high throughput demands sometimes ``PersistentActor`` and it's -``persist`` may not be enough in terms of consuming incoming Commands at a high rate, because it has to wait until all -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 -want to process commands as fast as you can, assuming that Event will eventually be persisted and handled properly in -the background and retroactively reacting to persistence failures if needed. - -The ``persistAsync`` method provides a tool for implementing high-throughput processors. It will *not* -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.). - -.. includecode:: code/docs/persistence/PersistenceDocTest.java#persist-async - -Notice that the client does not have to wrap any messages in the `Persistent` class in order to obtain "command sourcing like" -semantics. It's up to the processor to decide about persisting (or not) of messages, unlike ``Processor`` where the sender had to be aware of this decision. - -.. note:: - In order to implement the pattern known as "*command sourcing*" simply ``persistAsync`` all incoming events right away, - and handle them in the callback. - -.. _defer-java: - -Deferring actions until preceeding persist handlers have executed ------------------------------------------------------------------ - -Sometimes when working with ``persistAsync`` you may find that it would be nice to define some actions in terms of -''happens-after the previous ``persistAsync`` handlers have been invoked''. ``PersistentActor`` provides an utility method -called ``defer``, which works similarily to ``persistAsync`` yet does not persist the passed in event. It is recommended to -use it for *read* operations, and actions which do not have corresponding events in your domain model. - -Using this method is very similar to the persist family of methods, yet it does **not** persist the passed in event. -It will be kept in memory and used when invoking the handler. - -.. includecode:: code/docs/persistence/PersistenceDocTest.java#defer - -Notice that the ``sender()`` is **safe** to access in the handler callback, and will be pointing to the original sender -of the command for which this ``defer`` handler was called. - -.. includecode:: code/docs/persistence/PersistenceDocTest.java#defer-caller - - -Reliable event delivery ------------------------ - -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 ``receiveRecover``) must be sent to a -channel, as shown in the following example: - -.. includecode:: code/docs/persistence/PersistenceDocTest.java#reliable-event-delivery - -In larger integration scenarios, channel destinations may be actors that submit received events to an external -message broker, for example. After having successfully submitted an event, they should call ``confirm()`` on the -received ``ConfirmablePersistent`` message. - -Batch writes -============ - -To optimize throughput, an ``UntypedProcessor`` internally batches received ``Persistent`` messages under high load before -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-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 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`` 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 ``UntypedPersistentActor`` to ensure atomic -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 ``UntypedPersistentActor`` -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 =============== @@ -702,7 +447,7 @@ 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 and continue using the shared journal instance from the +example, allows persistent actors to failover to a backup node and continue using the shared journal instance from the backup node. .. warning:: @@ -729,7 +474,7 @@ done by calling the ``SharedLeveldbJournal.setStore`` method with the actor refe .. includecode:: code/docs/persistence/PersistencePluginDocTest.java#shared-store-usage -Internal journal commands (sent by processors) are buffered until injection completes. Injection is idempotent +Internal journal commands (sent by persistent actors) are buffered until injection completes. Injection is idempotent i.e. only the first injection is used. .. _local-snapshot-store-java: diff --git a/akka-docs/rst/project/migration-guide-eventsourced-2.3.x.rst b/akka-docs/rst/project/migration-guide-eventsourced-2.3.x.rst index d7a97a6d72..1778ace7c3 100644 --- a/akka-docs/rst/project/migration-guide-eventsourced-2.3.x.rst +++ b/akka-docs/rst/project/migration-guide-eventsourced-2.3.x.rst @@ -36,14 +36,14 @@ Eventsourced and Akka Persistence are both :ref:`extending-akka-scala`. **Akka Persistence:** ``Persistence`` extension - Must **not** be explicitly created by an application. A ``Persistence`` extension is implicitly created upon first - processor or channel creation. Journal actors are automatically created from a journal plugin configuration (see + `PersistentActor`` creation. Journal actors are automatically created from a journal plugin configuration (see :ref:`journal-plugin-api`). -- :ref:`processors` and :ref:`channels` can be created like any other actor with ``actorOf`` without using the +- ``PersistentActor`` can be created like any other actor with ``actorOf`` without using the ``Persistence`` extension. -- Is **not** a central registry of processors and channels. +- Is **not** a central registry of persistent actors. -Processors -========== +Processors / PersistentActor +============================ **Eventsourced:** ``Eventsourced`` @@ -70,30 +70,22 @@ Processors - Does not support batch-writes of messages to the journal. - Does not support stashing of messages. -**Akka Persistence:** ``Processor`` +**Akka Persistence:** ``PersistentActor`` -- Trait that adds journaling (write-ahead-logging) to actors (see :ref:`processors`) and used by applications for - *command sourcing*. Corresponds to ``Eventsourced`` processors in Eventsourced but is not a stackable trait. +- Trait that adds journaling to actors (see :ref:`event-sourcing`) and used by applications for + *event sourcing* or *command sourcing*. Corresponds to ``Eventsourced`` processors in Eventsourced but is not a stackable trait. - Automatically recovers on start and re-start, by default. :ref:`recovery` can be customized or turned off by overriding actor life cycle hooks ``preStart`` and ``preRestart``. ``Processor`` takes care that new messages never interfere with replayed messages. New messages are internally buffered until recovery completes. - No special-purpose behavior change methods. Default behavior change methods ``context.become`` and ``context.unbecome`` can be used and are journaling-preserving. -- Writes messages of type ``Persistent`` to the journal (see :ref:`persistent-messages`). Corresponds to ``Message`` - in Eventsourced. Sender references are written to the journal. A reply to senders must therefore be done via a - channel in order to avoid redundant replies during replay. Sender references of type ``PromiseActorRef`` are +- Sender references are written to the journal. Sender references of type ``PromiseActorRef`` are not journaled, they are ``system.deadLetters`` on replay. - Supports :ref:`snapshots`. - :ref:`persistence-identifiers` are of type ``String``, have a default value and can be overridden by applications. - Supports :ref:`batch-writes`. - Supports stashing of messages. -**Akka Persistence:** ``EventsourcedProcessor`` - -- Extension trait and pattern on top of ``Processor`` to support :ref:`event-sourcing`. Has no direct counterpart in - Eventsourced. Can be considered as a replacement of two processors in Eventsourced where one processor processes - commands and the other processes events that have been emitted by the command processor. - Channels ======== @@ -112,25 +104,6 @@ Channels - Does not redeliver messages on missing or negative delivery confirmation. - Cannot be used standalone. -**Akka Persistence:** ``Channel`` - -- Prevents redundant delivery of messages to a destination (see :ref:`channels`) i.e. serves the same primary purpose - as in Eventsourced. -- Is not associated with a single destination. A destination can be specified with each ``Deliver`` request and is - referred to by an actor path. A destination path is resolved to the current destination incarnation during delivery - (via ``actorSelection``). -- Must not be explicitly activated. Also, a network of processors and channels automatically recover consistently, - even if they are distributed. This enhancement, together with improved processor recovery, makes recovery of complex - Akka Persistence applications trivial. No special recovery procedures must be run by applications. -- Redelivers messages on missing delivery confirmation (see :ref:`redelivery`). In contrast to Eventsourced, Akka - Persistence doesn't distinguish between missing and negative confirmations. It only has a notion of missing - confirmations using timeouts (which are closely related to negative confirmations as both trigger message - redelivery). -- Can be used standalone. - -Persistent channels -=================== - **Eventsourced:** ``ReliableChannel`` - Provides ``DefaultChannel`` functionality plus persistence and recovery from sender JVM crashes (see `ReliableChannel @@ -142,16 +115,17 @@ Persistent channels - Cannot reply on persistence. - Can be used standalone. -**Akka Persistence:** ``PersistentChannel`` +**Akka Persistence:** ``AtLeastOnceDelivery`` -- Provides ``Channel`` functionality plus persistence and recovery from sender JVM crashes (see - :ref:`persistent-channels`). Same message redelivery features as ``Channel``. -- Redelivers unconfirmed messages concurrently to newly delivered messages. Flow control is done by channel using - a configurable minimum and maximum number of pending confirmations. -- Optionally notifies applications about messages for which the maximum number of delivery attempts has been reached - (also offered by ``Channel``). -- Can reply on persistence (= accept acknowledgement). -- Can be used standalone. +- ``AtLeastOnceDelivery`` trait is mixed in to a ``PersistentActor`` +- Does not prevent redundant delivery of messages to a destination +- Is not associated with a single destination. A destination can be specified with each ``deliver`` request and is + referred to by an actor path. A destination path is resolved to the current destination incarnation during delivery + (via ``actorSelection``). +- Redelivers messages on missing delivery confirmation. In contrast to Eventsourced, Akka + Persistence doesn't distinguish between missing and negative confirmations. It only has a notion of missing + confirmations using timeouts (which are closely related to negative confirmations as both trigger message + redelivery). Views ===== @@ -162,10 +136,9 @@ Views **Akka Persistence:** ``View`` -- Receives the message stream written by a ``Processor`` or ``EventsourcedProcessor`` by reading it directly from the +- Receives the message stream written by a ``PersistentActor`` by reading it directly from the journal (see :ref:`views`). Alternative to using channels. Useful in situations where actors shall receive a persistent message stream in correct order without duplicates. -- Can be used in combination with :ref:`channels` for sending messages. - Supports :ref:`snapshots`. Serializers @@ -194,7 +167,7 @@ Sequence numbers **Akka Persistence:** -- Generated on a per-processor basis. +- Generated on a per persistent actor basis. Storage plugins =============== diff --git a/akka-docs/rst/scala/code/docs/persistence/PersistenceDocSpec.scala b/akka-docs/rst/scala/code/docs/persistence/PersistenceDocSpec.scala index 42515eeb92..82baecbf0b 100644 --- a/akka-docs/rst/scala/code/docs/persistence/PersistenceDocSpec.scala +++ b/akka-docs/rst/scala/code/docs/persistence/PersistenceDocSpec.scala @@ -58,7 +58,7 @@ trait PersistenceDocSpec { } new AnyRef { - trait MyProcessor1 extends Processor { + trait MyProcessor1 extends PersistentActor { //#recover-on-start-disabled override def preStart() = () //#recover-on-start-disabled @@ -67,7 +67,7 @@ trait PersistenceDocSpec { //#recover-on-restart-disabled } - trait MyProcessor2 extends Processor { + trait MyProcessor2 extends PersistentActor { //#recover-on-start-custom override def preStart() { self ! Recover(toSequenceNr = 457L) @@ -75,7 +75,7 @@ trait PersistenceDocSpec { //#recover-on-start-custom } - trait MyProcessor3 extends Processor { + trait MyProcessor3 extends PersistentActor { //#deletion override def preRestart(reason: Throwable, message: Option[Any]) { message match { @@ -87,14 +87,16 @@ trait PersistenceDocSpec { //#deletion } - class MyProcessor4 extends Processor { + class MyProcessor4 extends PersistentActor { //#recovery-completed - def receive = initializing - def initializing: Receive = { - case RecoveryCompleted => - recoveryCompleted() - context.become(active) + def receiveRecover: Receive = { + case evt => //... + } + + def receiveCommand: Receive = { + case RecoveryCompleted => recoveryCompleted() + case msg => //... } def recoveryCompleted(): Unit = { @@ -102,9 +104,6 @@ trait PersistenceDocSpec { // ... } - def active: Receive = { - case Persistent(msg, _) => //... - } //#recovery-completed } } diff --git a/akka-docs/rst/scala/persistence.rst b/akka-docs/rst/scala/persistence.rst index d0a1755528..89d79306f8 100644 --- a/akka-docs/rst/scala/persistence.rst +++ b/akka-docs/rst/scala/persistence.rst @@ -11,7 +11,7 @@ persistence is that only changes to an actor's internal state are persisted but 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. +communication with at-least-once message delivery semantics. .. warning:: @@ -36,76 +36,114 @@ Akka persistence is a separate jar file. Make sure that you have the following d Architecture ============ -* *Processor* (deprecated, use *PersistentActor* instead): A processor is a persistent, stateful actor. Messages sent - to a processor are written to a journal 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. - * *PersistentActor*: Is a persistent, stateful actor. It is able to persist events to a journal and can react to 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 recover internal state from these messages. * *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 + persistent actor. A view itself does not journal new messages, instead, it updates internal state only from a persistent actor's replicated message stream. -* *Streams*: Messages written by a processor can be published in compliance with the `Reactive Streams`_ specification. - Only those messages that are explicitly requested from downstream processors are actually pulled from a processor's +* *Streams*: Messages written by a persistent actor can be published in compliance with the `Reactive Streams`_ specification. + Only those messages that are explicitly requested from downstream persistent actors are actually pulled from a persistent actor's journal. -* *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 +* *AtLeastOnceDelivery*: To send messages with at-least-once delivery semantics to destinations, 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 journaled and which are received by the processor without being journaled. The storage backend of a journal is +* *Journal*: 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. The storage backend of a journal is pluggable. The default journal storage plugin writes to the local filesystem, replicated journals are available as `Community plugins`_. -* *Snapshot store*: A snapshot store persists snapshots of a processor's or a view's internal state. Snapshots are +* *Snapshot store*: A snapshot store persists snapshots of a persistent actor'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`) - .. _Community plugins: http://akka.io/community/ .. _Reactive Streams: http://www.reactive-streams.org/ -.. _processors: -Processors -========== +.. _event-sourcing: -.. warning:: - ``Processor`` is deprecated. Instead the current ``PersistentActor`` will be extended to provide equivalent - functionality if required (by introducing the ``persistAsync`` method). - For details see `Relaxed local consistency requirements and high throughput use-cases`_ as well as the discussion - and pull requests related to this `issue on Github `_. +Event sourcing +============== -A processor can be implemented by extending the ``Processor`` trait and implementing the ``receive`` method. +The basic idea behind `Event Sourcing`_ is quite simple. A persistent actor receives a (non-persistent) command +which is first validated if it can be applied to the current state. Here, validation can mean anything, from simple +inspection of a command message's fields up to a conversation with several external services, for example. +If validation succeeds, events are generated from the command, representing the effect of the command. These events +are then persisted and, after successful persistence, used to change the actor's state. When the persistent actor +needs to be recovered, only the persisted events are replayed of which we know that they can be successfully applied. +In other words, events cannot fail when being replayed to a persistent actor, in contrast to commands. Event sourced +actors may of course also process commands that do not change application state, such as query commands, for example. -.. includecode:: code/docs/persistence/PersistenceDocSpec.scala#definition +.. _Event Sourcing: http://martinfowler.com/eaaDev/EventSourcing.html -Processors only write messages of type ``Persistent`` to the journal, others are received without being persisted. -When a processor's ``receive`` 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 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. +Akka persistence supports event sourcing with the ``PersistentActor`` trait. An actor that extends this trait uses the +``persist`` method to persist and handle events. The behavior of a ``PersistentActor`` +is defined by implementing ``receiveRecover`` and ``receiveCommand``. This is demonstrated in the following example. -A ``Processor`` itself is an ``Actor`` and can therefore be instantiated with ``actorOf``. +.. includecode:: ../../../akka-samples/akka-sample-persistence-scala/src/main/scala/sample/persistence/PersistentActorExample.scala#persistent-actor-example -.. includecode:: code/docs/persistence/PersistenceDocSpec.scala#usage +The example defines two data types, ``Cmd`` and ``Evt`` to represent commands and events, respectively. The +``state`` of the ``ExampleProcessor`` is a list of persisted event data contained in ``ExampleState``. + +The persistent actor's ``receiveRecover`` method defines how ``state`` is updated during recovery by handling ``Evt`` +and ``SnapshotOffer`` messages. The persistent actor's ``receiveCommand`` method is a command handler. In this example, +a command is handled by generating two events which are then persisted and handled. Events are persisted by calling +``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 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. + +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`` +calls in context of a single command. + +The easiest way to run this example yourself is to download `Typesafe Activator `_ +and open the tutorial named `Akka Persistence Samples with Scala `_. +It contains instructions on how to run the ``PersistentActorExample``. + +.. note:: + + It's also possible to switch between different command handlers during normal processing and recovery + with ``context.become()`` and ``context.unbecome()``. To get the actor into the same state after + recovery you need to take special care to perform the same state transitions with ``become`` and + ``unbecome`` in the ``receiveRecover`` method as you would have done in the command handler. + +Identifiers +----------- + +A persistent actor must have an identifier that doesn't change across different actor incarnations. It defaults to the +``String`` representation of persistent actor's path without the address part and can be obtained via the ``persistenceId`` +method. + +.. includecode:: code/docs/persistence/PersistenceDocSpec.scala#persistence-id + +Applications can customize a persistent actor's id by specifying an actor name during persistent actor creation as shown in +section :ref:`event-sourcing`. This changes that persistent actor's name in its actor hierarchy and hence influences only +part of the persistent actor id. To fully customize a persistent actor's id, the ``persistenceId`` method must be overridden. + +.. includecode:: code/docs/persistence/PersistenceDocSpec.scala#persistence-id-override + +Overriding ``persistenceId`` is the recommended way to generate stable identifiers. .. _recovery: Recovery -------- -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 a processor after recovery completes. +By default, a persistent actor is automatically recovered on start and on restart by replaying journaled messages. +New messages sent to a persistent actor during recovery do not interfere with replayed messages. New messages will +only be received by a persistent actor after recovery completes. Recovery customization ^^^^^^^^^^^^^^^^^^^^^^ @@ -114,7 +152,7 @@ Automated recovery on start can be disabled by overriding ``preStart`` with an e .. includecode:: code/docs/persistence/PersistenceDocSpec.scala#recover-on-start-disabled -In this case, a processor must be recovered explicitly by sending it a ``Recover()`` message. +In this case, a persistent actor must be recovered explicitly by sending it a ``Recover()`` message. .. includecode:: code/docs/persistence/PersistenceDocSpec.scala#recover-explicit @@ -123,7 +161,7 @@ If not overridden, ``preStart`` sends a ``Recover()`` message to ``self``. Appli .. includecode:: code/docs/persistence/PersistenceDocSpec.scala#recover-on-start-custom -Upper sequence number bounds can be used to recover a processor to past state instead of current state. Automated +Upper sequence number bounds can be used to recover a persistent actor to past state instead of current state. Automated recovery on restart can be disabled by overriding ``preRestart`` with an empty implementation. .. includecode:: code/docs/persistence/PersistenceDocSpec.scala#recover-on-restart-disabled @@ -131,57 +169,99 @@ recovery on restart can be disabled by overriding ``preRestart`` with an empty i Recovery status ^^^^^^^^^^^^^^^ -A processor can query its own recovery status via the methods +A persistent actor can query its own recovery status via the methods .. includecode:: code/docs/persistence/PersistenceDocSpec.scala#recovery-status Sometimes there is a need for performing additional initialization when the -recovery has completed, before processing any other message sent to the processor. -The processor will receive a special :class:`RecoveryCompleted` message right after recovery -and before any other received messages. If there is a problem with recovering the state of -the actor from the journal, the actor will be sent a :class:`RecoveryFailure` message that -it can choose to handle. If the actor doesn't handle the :class:`RecoveryFailure` message it -will be stopped. +recovery has completed, before processing any other message sent to the persistent actor. +The persistent actor will receive a special :class:`RecoveryCompleted` message right after recovery +and before any other received messages. + +If there is a problem with recovering the state of the actor from the journal, the actor will be +sent a :class:`RecoveryFailure` message that it can choose to handle in ``receiveRecover``. If the +actor doesn't handle the :class:`RecoveryFailure` message it will be stopped. .. includecode:: code/docs/persistence/PersistenceDocSpec.scala#recovery-completed +.. _persist-async-scala: -.. _failure-handling: +Relaxed local consistency requirements and high throughput use-cases +-------------------------------------------------------------------- -Failure handling -^^^^^^^^^^^^^^^^ +If faced with relaxed local consistency requirements and high throughput demands sometimes ``PersistentActor`` and it's +``persist`` may not be enough in terms of consuming incoming Commands at a high rate, because it has to wait until all +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 +want to process commands as fast as you can, assuming that Event will eventually be persisted and handled properly in +the background and retroactively reacting to persistence failures if needed. -A persistent message that caused an exception will be received again by a processor after restart. To prevent -a replay of that message during recovery it can be deleted. +The ``persistAsync`` method provides a tool for implementing high-throughput persistent actors. It will *not* +stash incoming Commands while the Journal is still working on persisting and/or user code is executing event callbacks. -.. includecode:: code/docs/persistence/PersistenceDocSpec.scala#deletion +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.). + +.. includecode:: code/docs/persistence/PersistenceDocSpec.scala#persist-async + +.. note:: + In order to implement the pattern known as "*command sourcing*" simply call ``persistAsync(cmd)(...)`` right away on all incomming + messages right away, and handle them in the callback. + +.. _defer-scala: + +Deferring actions until preceeding persist handlers have executed +----------------------------------------------------------------- + +Sometimes when working with ``persistAsync`` you may find that it would be nice to define some actions in terms of +''happens-after the previous ``persistAsync`` handlers have been invoked''. ``PersistentActor`` provides an utility method +called ``defer``, which works similarily to ``persistAsync`` yet does not persist the passed in event. It is recommended to +use it for *read* operations, and actions which do not have corresponding events in your domain model. + +Using this method is very similar to the persist family of methods, yet it does **not** persist the passed in event. +It will be kept in memory and used when invoking the handler. + +.. includecode:: code/docs/persistence/PersistenceDocSpec.scala#defer + +Notice that the ``sender()`` is **safe** to access in the handler callback, and will be pointing to the original sender +of the command for which this ``defer`` handler was called. + +The calling side will get the responses in this (guaranteed) order: + +.. includecode:: code/docs/persistence/PersistenceDocSpec.scala#defer-caller + + +.. _batch-writes: + +Batch writes +------------ + +To optimize throughput, a persistent actor internally batches events to be stored under high load before +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. When using ``persistAsync`` this increases +the maximum throughput dramatically. + +.. includecode:: code/docs/persistence/PersistencePluginDocSpec.scala#max-message-batch-size + +A new batch write is triggered by a persistent actor 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 at a minimum. + +The batches are also used internally to ensure atomic 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 a ``PersistentActor`` will therefore never be done partially (with only a subset of events persisted by a +single command). Message deletion ---------------- -A processor can delete a single message by calling the ``deleteMessage`` method with the sequence number of +A persistent actor can delete a single message by calling the ``deleteMessage`` method with the sequence number of that message as argument. An optional ``permanent`` parameter specifies whether the message shall be permanently 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 should call the ``deleteMessages`` method. +purposes, for example. To delete all messages (journaled by a single persistent actor) up to a specified sequence number, +persistent actors should call the ``deleteMessages`` method. -Identifiers ------------ -A processor must have an identifier that doesn't change across different actor incarnations. It defaults to the -``String`` representation of processor's path without the address part and can be obtained via the ``persistenceId`` -method. - -.. includecode:: code/docs/persistence/PersistenceDocSpec.scala#persistence-id - -Applications can customize a processor's id by specifying an actor name during processor creation as shown in -section :ref:`processors`. 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 ``persistenceId`` method must be overridden. - -.. includecode:: code/docs/persistence/PersistenceDocSpec.scala#persistence-id-override - -Overriding ``persistenceId`` is the recommended way to generate stable identifiers. .. _views: @@ -193,9 +273,9 @@ methods. .. includecode:: code/docs/persistence/PersistenceDocSpec.scala#view -The ``persistenceId`` 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 +The ``persistenceId`` identifies the persistent actor from which the view receives journaled messages. It is not necessary +the referenced persistent actor is actually running. Views read messages from a persistent actor's journal directly. When a +persistent actor is started later and begins to write new messages, the corresponding view is updated automatically, by default. Updates @@ -228,9 +308,11 @@ of replayed messages for manual updates can be limited with the ``replayMax`` pa Recovery -------- -Initial recovery of views works in the very same way as for :ref:`processors` (i.e. by sending a ``Recover`` message +Initial recovery of views works in the very same way as for a persistent actor (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`. +Further possibilities to customize initial recovery are explained in section :ref:`recovery`. + +.. _persistence-identifiers: Identifiers ----------- @@ -244,7 +326,7 @@ name in its actor hierarchy and hence influences only part of the view id. To fu ``viewId`` method must be overridden. Overriding ``viewId`` is the recommended way to generate stable identifiers. The ``viewId`` must differ from the referenced ``persistenceId``, unless :ref:`snapshots` of a view and its -processor shall be shared (which is what applications usually do not want). +persistent actor shall be shared (which is what applications usually do not want). .. _streams: @@ -253,7 +335,7 @@ Streams **TODO: rename *producer* to *publisher*.** -A `Reactive Streams`_ ``Producer`` can be created from a processor's message stream via the ``PersistentFlow`` +A `Reactive Streams`_ ``Producer`` can be created from a persistent actor's message stream via the ``PersistentFlow`` extension of the Akka Streams Scala DSL: .. includecode:: code/docs/persistence/PersistenceDocSpec.scala#producer-creation @@ -262,8 +344,8 @@ The created ``flow`` object is of type ``Flow[Persistent]`` and can be composed combinators (= methods defined on ``Flow``). Calling the ``toProducer`` method on ``flow`` creates a producer of type ``Producer[Persistent]``. -A persistent message producer only reads from a processor's journal when explicitly requested by downstream -consumers. In order to avoid frequent, fine grained read access to a processor's journal, the producer tries +A persistent message producer only reads from a persistent actor's journal when explicitly requested by downstream +consumers. In order to avoid frequent, fine grained read access to a persistent actor's journal, the producer tries to buffer persistent messages in memory from which it serves downstream requests. The maximum buffer size per producer is configurable with a ``PersistentPublisherSettings`` configuration object. @@ -284,205 +366,16 @@ Streams Scala DSL and its ``PersistentFlow`` extension. .. includecode:: code/docs/persistence/PersistenceDocSpec.scala#producer-examples -.. _channels: - -Channels -======== - -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. - -Channels prevent redundant delivery of replayed messages to destinations during processor recovery. A replayed -message is retained by a channel if its delivery has been confirmed by a destination. - -.. includecode:: code/docs/persistence/PersistenceDocSpec.scala#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. 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. - -.. note:: - - Sending via a channel has at-least-once delivery semantics—by virtue of either - the sending actor or the channel being persistent—which means that the - semantics do not match those of a normal :class:`ActorRef` send operation: - - * it is not at-most-once delivery - - * message order for the same sender–receiver pair is not retained due to - possible resends - - * after a crash and restart of the destination messages are still - delivered—to the new actor incarnation - - These semantics match precisely what an :class:`ActorPath` represents (see - :ref:`actor-lifecycle-scala`), therefore you need to supply a path and not a - reference when constructing :class:`Deliver` messages. - -If a processor wants to reply to a ``Persistent`` message sender it should use the ``sender`` path as channel -destination. - -.. includecode:: code/docs/persistence/PersistenceDocSpec.scala#channel-example-reply - -Persistent messages delivered by a channel are of type ``ConfirmablePersistent``. ``ConfirmablePersistent`` extends -``Persistent`` by adding the methods ``confirm`` and ``redeliveries`` (see also :ref:`redelivery`). 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. - -.. _redelivery: - -Message re-delivery -------------------- - -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. The number of re-delivery -attempts can be obtained via the ``redeliveries`` method on ``ConfirmablePersistent`` or by pattern matching. - -.. includecode:: code/docs/persistence/PersistenceDocSpec.scala#channel-custom-settings - -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. - -.. includecode:: code/docs/persistence/PersistenceDocSpec.scala#channel-custom-listener - -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 - -* message persistence by sending processors -* message replays by sending processors -* message re-deliveries by channels and -* application-level confirmations (acknowledgements) by destinations - -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. - -.. warning:: - - 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 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. - -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: - -Persistent channels -------------------- - -Channels created with ``Channel.props`` do not persist messages. These channels are usually used in combination -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. - -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/PersistenceDocSpec.scala#persistent-channel-example - -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``. - -.. includecode:: code/docs/persistence/PersistenceDocSpec.scala#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/PersistenceDocSpec.scala#persistent-channel-reply - -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. - -.. _persistence-identifiers: - -Identifiers ------------ - -In the same way as :ref:`processors` and :ref:`views`, 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/PersistenceDocSpec.scala#channel-id-override - -.. _persistent-messages: - -Persistent messages -=================== - -Payload -------- - -The payload of a ``Persistent`` message can be obtained via its - -.. includecode:: ../../../akka-persistence/src/main/scala/akka/persistence/Persistent.scala#payload - -method or by pattern matching - -.. includecode:: code/docs/persistence/PersistenceDocSpec.scala#payload-pattern-matching - -Inside processors, new persistent messages are derived from the current persistent message before sending them via a -channel, either by calling ``p.withPayload(...)`` or ``Persistent(...)`` where the latter uses the -implicit ``currentPersistentMessage`` made available by ``Processor``. - -.. includecode:: code/docs/persistence/PersistenceDocSpec.scala#current-message - -This is necessary for delivery confirmations to work properly. Both ways are equivalent but we recommend -using ``p.withPayload(...)`` for clarity. - -Sequence number ---------------- - -The sequence number of a ``Persistent`` message can be obtained via its - -.. includecode:: ../../../akka-persistence/src/main/scala/akka/persistence/Persistent.scala#sequence-nr - -method or by pattern matching - -.. includecode:: code/docs/persistence/PersistenceDocSpec.scala#sequence-nr-pattern-matching - -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 messages. - .. _snapshots: Snapshots ========= -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. +Snapshots can dramatically reduce recovery times of persistent actors and views. The following discusses snapshots +in context of persistent actors 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 +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 .. includecode:: code/docs/persistence/PersistenceDocSpec.scala#save-snapshot @@ -490,15 +383,15 @@ where ``metadata`` is of type ``SnapshotMetadata``: .. includecode:: ../../../akka-persistence/src/main/scala/akka/persistence/Snapshot.scala#snapshot-metadata -During recovery, the processor is offered a previously saved snapshot via a ``SnapshotOffer`` message from +During recovery, the persistent actor is offered a previously saved snapshot via a ``SnapshotOffer`` message from which it can initialize internal state. .. includecode:: code/docs/persistence/PersistenceDocSpec.scala#snapshot-offer The replayed messages that follow the ``SnapshotOffer`` message, if any, are younger than the offered snapshot. -They finally recover the processor to its current (i.e. latest) state. +They finally recover the persistent actor to its current (i.e. latest) state. -In general, a processor is only offered a snapshot if that processor has previously saved one or more snapshots +In general, a persistent actor is only offered a snapshot if that persistent actor has previously saved one or more snapshots and at least one of these snapshots matches the ``SnapshotSelectionCriteria`` that can be specified for recovery. .. includecode:: code/docs/persistence/PersistenceDocSpec.scala#snapshot-criteria @@ -510,169 +403,10 @@ saved snapshot matches the specified ``SnapshotSelectionCriteria`` will replay a Snapshot deletion ----------------- -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 +A persistent actor 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``, persistent actors should use the ``deleteSnapshots`` method. -.. _event-sourcing: - -Event sourcing -============== - -.. note:: - The ``PersistentActor`` introduced in this section was previously known as ``EventsourcedProcessor`` - which was a subset of the ``PersistentActor``. Migrating your code to use persistent actors instead is - very simple and is explained in the :ref:`migration-guide-persistence-experimental-2.3.x-2.4.x`. - -In all the examples so far, messages that change a processor's state have been sent as ``Persistent`` messages -by an application, so that they can be replayed during recovery. From this point of view, the journal acts as -a write-ahead-log for whatever ``Persistent`` messages a processor receives. This is also known as *command -sourcing*. Commands, however, may fail and some applications cannot tolerate command failures during recovery. - -For these applications `Event Sourcing`_ is a better choice. Applied to Akka persistence, the basic idea behind -event sourcing is quite simple. A processor receives a (non-persistent) command which is first validated if it -can be applied to the current state. Here, validation can mean anything, from simple inspection of a command -message's fields up to a conversation with several external services, for example. If validation succeeds, events -are generated from the command, representing the effect of the command. These events are then persisted and, after -successful persistence, used to change a processor's state. When the processor needs to be recovered, only the -persisted events are replayed of which we know that they can be successfully applied. In other words, events -cannot fail when being replayed to a processor, in contrast to commands. Eventsourced processors may of course -also process commands that do not change application state, such as query commands, for example. - -.. _Event Sourcing: http://martinfowler.com/eaaDev/EventSourcing.html - -Akka persistence supports event sourcing with the ``PersistentActor`` trait (which implements event sourcing -as a pattern on top of command sourcing). A processor that extends this trait does not handle ``Persistent`` messages -directly but uses the ``persist`` method to persist and handle events. The behavior of an ``PersistentActor`` -is defined by implementing ``receiveRecover`` and ``receiveCommand``. This is demonstrated in the following example. - -.. includecode:: ../../../akka-samples/akka-sample-persistence-scala/src/main/scala/sample/persistence/PersistentActorExample.scala#persistent-actor-example - -The example defines two data types, ``Cmd`` and ``Evt`` to represent commands and events, respectively. The -``state`` of the ``ExampleProcessor`` is a list of persisted event data contained in ``ExampleState``. - -The processor's ``receiveRecover`` method defines how ``state`` is updated during recovery by handling ``Evt`` -and ``SnapshotOffer`` messages. The processor's ``receiveCommand`` method is a command handler. In this example, -a command is handled by generating two events which are then persisted and handled. Events are persisted by calling -``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 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 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 easiest way to run this example yourself is to download `Typesafe Activator `_ -and open the tutorial named `Akka Persistence Samples with Scala `_. -It contains instructions on how to run the ``PersistentActorExample``. - -.. note:: - - It's also possible to switch between different command handlers during normal processing and recovery - with ``context.become()`` and ``context.unbecome()``. To get the actor into the same state after - recovery you need to take special care to perform the same state transitions with ``become`` and - ``unbecome`` in the ``receiveRecover`` method as you would have done in the command handler. - -.. _persist-async-scala: - -Relaxed local consistency requirements and high throughput use-cases --------------------------------------------------------------------- - -If faced with Relaxed local consistency requirements and high throughput demands sometimes ``PersistentActor`` and it's -``persist`` may not be enough in terms of consuming incoming Commands at a high rate, because it has to wait until all -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 -want to process commands as fast as you can, assuming that Event will eventually be persisted and handled properly in -the background and retroactively reacting to persistence failures if needed. - -The ``persistAsync`` method provides a tool for implementing high-throughput processors. It will *not* -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.). - -.. includecode:: code/docs/persistence/PersistenceDocSpec.scala#persist-async - -Notice that the client does not have to wrap any messages in the `Persistent` class in order to obtain "command sourcing like" -semantics. It's up to the processor to decide about persisting (or not) of messages, unlike ``Processor`` where the sender had to be aware of this decision. - -.. note:: - In order to implement the "*command sourcing*" simply call ``persistAsync(cmd)(...)`` right away on all incomming - messages right away, and handle them in the callback. - -.. _defer-scala: - -Deferring actions until preceeding persist handlers have executed ------------------------------------------------------------------ - -Sometimes when working with ``persistAsync`` you may find that it would be nice to define some actions in terms of -''happens-after the previous ``persistAsync`` handlers have been invoked''. ``PersistentActor`` provides an utility method -called ``defer``, which works similarily to ``persistAsync`` yet does not persist the passed in event. It is recommended to -use it for *read* operations, and actions which do not have corresponding events in your domain model. - -Using this method is very similar to the persist family of methods, yet it does **not** persist the passed in event. -It will be kept in memory and used when invoking the handler. - -.. includecode:: code/docs/persistence/PersistenceDocSpec.scala#defer - -Notice that the ``sender()`` is **safe** to access in the handler callback, and will be pointing to the original sender -of the command for which this ``defer`` handler was called. - -The calling side will get the responses in this (guaranteed) order: - -.. includecode:: code/docs/persistence/PersistenceDocSpec.scala#defer-caller - -Reliable event delivery ------------------------ - -Sending events from an event handler to another actor has at-most-once delivery semantics. For at-least-once delivery, -:ref:`channels` must be used. In this case, also replayed events (received by ``receiveRecover``) must be sent to a -channel, as shown in the following example: - -.. includecode:: code/docs/persistence/PersistenceDocSpec.scala#reliable-event-delivery - -In larger integration scenarios, channel destinations may be actors that submit received events to an external -message broker, for example. After having successfully submitted an event, they should call ``confirm()`` on the -received ``ConfirmablePersistent`` message. - -.. _batch-writes: - -Batch writes -============ - -To optimize throughput, a ``Processor`` internally batches received ``Persistent`` messages under high load before -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:: 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 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/PersistenceDocSpec.scala#batch-write - -``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 ``PersistentActor`` to ensure atomic -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 ``PersistentActor`` -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` 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: @@ -754,7 +488,7 @@ 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 and continue using the shared journal instance from the +example, allows persistent actors to failover to a backup node and continue using the shared journal instance from the backup node. .. warning:: @@ -781,7 +515,7 @@ done by calling the ``SharedLeveldbJournal.setStore`` method with the actor refe .. includecode:: code/docs/persistence/PersistencePluginDocSpec.scala#shared-store-usage -Internal journal commands (sent by processors) are buffered until injection completes. Injection is idempotent +Internal journal commands (sent by persistent actors) are buffered until injection completes. Injection is idempotent i.e. only the first injection is used. .. _local-snapshot-store: @@ -832,7 +566,7 @@ Miscellaneous State machines -------------- -State machines can be persisted by mixing in the ``FSM`` trait into processors. +State machines can be persisted by mixing in the ``FSM`` trait into persistent actors. .. includecode:: code/docs/persistence/PersistenceDocSpec.scala#fsm-example diff --git a/akka-persistence/src/main/scala/akka/persistence/Eventsourced.scala b/akka-persistence/src/main/scala/akka/persistence/Eventsourced.scala index ac8a05b658..ffa3fa5ab5 100644 --- a/akka-persistence/src/main/scala/akka/persistence/Eventsourced.scala +++ b/akka-persistence/src/main/scala/akka/persistence/Eventsourced.scala @@ -542,6 +542,7 @@ abstract class UntypedEventsourcedProcessor extends UntypedProcessor with Events * * @see [[Recover]] */ + @throws(classOf[Exception]) def onReceiveRecover(msg: Any): Unit /** @@ -552,6 +553,7 @@ abstract class UntypedEventsourcedProcessor extends UntypedProcessor with Events * [[ResequenceableBatch]] messages. In this case an `UnsupportedOperationException` is * thrown by the processor. */ + @throws(classOf[Exception]) def onReceiveCommand(msg: Any): Unit } diff --git a/akka-persistence/src/main/scala/akka/persistence/Processor.scala b/akka-persistence/src/main/scala/akka/persistence/Processor.scala index 7bc180e79b..ec69966917 100644 --- a/akka-persistence/src/main/scala/akka/persistence/Processor.scala +++ b/akka-persistence/src/main/scala/akka/persistence/Processor.scala @@ -90,7 +90,7 @@ trait Processor extends Actor with Recovery { case ReplayedMessage(p) ⇒ processPersistent(receive, p) // can occur after unstash from user stash case WriteMessageSuccess(p: PersistentRepr) ⇒ processPersistent(receive, p) case WriteMessageSuccess(r: Resequenceable) ⇒ process(receive, r) - case WriteMessageFailure(p, cause) ⇒ + case WriteMessageFailure(p, cause) ⇒ process(receive, PersistenceFailure(p.payload, p.sequenceNr, cause)) case LoopMessageSuccess(m) ⇒ process(receive, m) case WriteMessagesSuccessful | WriteMessagesFailed(_) ⇒ @@ -156,7 +156,7 @@ trait Processor extends Actor with Recovery { */ private def onRecoveryCompleted(receive: Receive): Unit = receive.applyOrElse(RecoveryCompleted, unhandled) - + private val _persistenceId = extension.persistenceId(self) private var processorBatch = Vector.empty[Resequenceable] diff --git a/akka-samples/akka-sample-persistence-java-lambda/src/main/java/doc/LambdaPersistenceDocTest.java b/akka-samples/akka-sample-persistence-java-lambda/src/main/java/doc/LambdaPersistenceDocTest.java index 09e6753e24..579d859a9d 100644 --- a/akka-samples/akka-sample-persistence-java-lambda/src/main/java/doc/LambdaPersistenceDocTest.java +++ b/akka-samples/akka-sample-persistence-java-lambda/src/main/java/doc/LambdaPersistenceDocTest.java @@ -136,27 +136,31 @@ public class LambdaPersistenceDocTest { } //#recovery-completed - class MyProcessor5 extends AbstractProcessor { - - public MyProcessor5() { - receive(ReceiveBuilder. - match(RecoveryCompleted.class, r -> { - recoveryCompleted(); - getContext().become(active); - }). - build() - ); + class MyPersistentActor5 extends AbstractPersistentActor { + + @Override public PartialFunction receiveRecover() { + return ReceiveBuilder. + match(String.class, this::handleEvent).build(); } + @Override public PartialFunction receiveCommand() { + return ReceiveBuilder. + match(RecoveryCompleted.class, r -> { + recoveryCompleted(); + }). + match(String.class, s -> s.equals("cmd"), + s -> persist("evt", this::handleEvent)).build(); + } + private void recoveryCompleted() { // perform init after recovery, before any other messages // ... } - PartialFunction active = - ReceiveBuilder. - match(Persistent.class, message -> {/* ... */}). - build(); + private void handleEvent(String event) { + // update state + // ... + } } //#recovery-completed diff --git a/akka-samples/akka-sample-persistence-java-lambda/src/main/java/sample/persistence/ConversationRecoveryExample.java b/akka-samples/akka-sample-persistence-java-lambda/src/main/java/sample/persistence/ConversationRecoveryExample.java deleted file mode 100644 index 7a748f9374..0000000000 --- a/akka-samples/akka-sample-persistence-java-lambda/src/main/java/sample/persistence/ConversationRecoveryExample.java +++ /dev/null @@ -1,76 +0,0 @@ -/** - * Copyright (C) 2009-2014 Typesafe Inc. - */ - -package sample.persistence; - -import akka.actor.ActorRef; -import akka.actor.ActorSystem; -import akka.actor.Props; -import akka.japi.pf.ReceiveBuilder; -import akka.persistence.*; -import scala.PartialFunction; -import scala.runtime.BoxedUnit; - -public class ConversationRecoveryExample { - public static String PING = "PING"; - public static String PONG = "PONG"; - - public static class Ping extends AbstractProcessor { - final ActorRef pongChannel = context().actorOf(Channel.props(), "pongChannel"); - int counter = 0; - - public Ping() { - receive(ReceiveBuilder. - match(ConfirmablePersistent.class, cp -> cp.payload().equals(PING), cp -> { - counter += 1; - System.out.println(String.format("received ping %d times", counter)); - cp.confirm(); - if (!recoveryRunning()) { - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - pongChannel.tell(Deliver.create(cp.withPayload(PONG), sender().path()), self()); - }). - match(String.class, - s -> s.equals("init"), - s -> pongChannel.tell(Deliver.create(Persistent.create(PONG), sender().path()), self())).build() - ); - } - } - - public static class Pong extends AbstractProcessor { - private final ActorRef pingChannel = context().actorOf(Channel.props(), "pingChannel"); - private int counter = 0; - - public Pong() { - receive(ReceiveBuilder. - match(ConfirmablePersistent.class, cp -> cp.payload().equals(PONG), cp -> { - counter += 1; - System.out.println(String.format("received pong %d times", counter)); - cp.confirm(); - if (!recoveryRunning()) { - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - pingChannel.tell(Deliver.create(cp.withPayload(PING), sender().path()), self()); - }).build() - ); - } - } - - public static void main(String... args) throws Exception { - final ActorSystem system = ActorSystem.create("example"); - - final ActorRef ping = system.actorOf(Props.create(Ping.class), "ping"); - final ActorRef pong = system.actorOf(Props.create(Pong.class), "pong"); - - ping.tell("init", pong); - } -} diff --git a/akka-samples/akka-sample-persistence-java-lambda/src/main/java/sample/persistence/PersistentActorExample.java b/akka-samples/akka-sample-persistence-java-lambda/src/main/java/sample/persistence/PersistentActorExample.java index a527504a46..7e4d75ab00 100644 --- a/akka-samples/akka-sample-persistence-java-lambda/src/main/java/sample/persistence/PersistentActorExample.java +++ b/akka-samples/akka-sample-persistence-java-lambda/src/main/java/sample/persistence/PersistentActorExample.java @@ -73,43 +73,46 @@ class ExampleState implements Serializable { } } -class ExampleProcessor extends AbstractPersistentActor { +class ExamplePersistentActor extends AbstractPersistentActor { private ExampleState state = new ExampleState(); public int getNumEvents() { return state.size(); } - @Override - public PartialFunction receiveRecover() { + @Override + public PartialFunction receiveRecover() { return ReceiveBuilder. match(Evt.class, state::update). match(SnapshotOffer.class, ss -> state = (ExampleState) ss.snapshot()).build(); } - @Override - public PartialFunction receiveCommand() { - return ReceiveBuilder.match(Cmd.class, c -> { - final String data = c.getData(); - final Evt evt1 = new Evt(data + "-" + getNumEvents()); - final Evt evt2 = new Evt(data + "-" + (getNumEvents() + 1)); - persist(asList(evt1, evt2), (Evt evt) -> { - state.update(evt); - if (evt.equals(evt2)) { - context().system().eventStream().publish(evt); - } - }); - }). + @Override + public PartialFunction receiveCommand() { + return ReceiveBuilder. + match(Cmd.class, c -> { + final String data = c.getData(); + final Evt evt1 = new Evt(data + "-" + getNumEvents()); + final Evt evt2 = new Evt(data + "-" + (getNumEvents() + 1)); + persist(asList(evt1, evt2), (Evt evt) -> { + state.update(evt); + if (evt.equals(evt2)) { + context().system().eventStream().publish(evt); + } + }); + }). match(String.class, s -> s.equals("snap"), s -> saveSnapshot(state.copy())). - match(String.class, s -> s.equals("print"), s -> System.out.println(state)).build(); + match(String.class, s -> s.equals("print"), s -> System.out.println(state)). + build(); } + } //#persistent-actor-example public class PersistentActorExample { public static void main(String... args) throws Exception { final ActorSystem system = ActorSystem.create("example"); - final ActorRef processor = system.actorOf(Props.create(ExampleProcessor.class), "processor-4-java8"); + final ActorRef processor = system.actorOf(Props.create(ExamplePersistentActor.class), "processor-4-java8"); processor.tell(new Cmd("foo"), null); processor.tell(new Cmd("baz"), null); processor.tell(new Cmd("bar"), null); diff --git a/akka-samples/akka-sample-persistence-java-lambda/src/main/java/sample/persistence/PersistentActorFailureExample.java b/akka-samples/akka-sample-persistence-java-lambda/src/main/java/sample/persistence/PersistentActorFailureExample.java new file mode 100644 index 0000000000..3c56040b44 --- /dev/null +++ b/akka-samples/akka-sample-persistence-java-lambda/src/main/java/sample/persistence/PersistentActorFailureExample.java @@ -0,0 +1,74 @@ +/** + * Copyright (C) 2009-2014 Typesafe Inc. + */ + +package sample.persistence; + +import akka.actor.ActorRef; +import akka.actor.ActorSystem; +import akka.actor.Props; +import akka.japi.pf.ReceiveBuilder; +import akka.persistence.AbstractPersistentActor; +import scala.PartialFunction; +import scala.runtime.BoxedUnit; + +import java.util.ArrayList; + +public class PersistentActorFailureExample { + public static class ExamplePersistentActor extends AbstractPersistentActor { + private ArrayList received = new ArrayList(); + + @Override + public PartialFunction receiveCommand() { + return ReceiveBuilder. + match(String.class, s -> s.equals("boom"), s -> {throw new RuntimeException("boom");}). + match(String.class, s -> s.equals("print"), s -> System.out.println("received " + received)). + match(String.class, s -> { + persist(s, evt -> { + received.add(evt); + }); + }). + build(); + } + + @Override + public PartialFunction receiveRecover() { + return ReceiveBuilder. + match(String.class, s -> received.add(s)). + build(); + } + + + } + + public static void main(String... args) throws Exception { + final ActorSystem system = ActorSystem.create("example"); + final ActorRef persistentActor = system.actorOf(Props.create(ExamplePersistentActor.class), "persistentActor-2"); + + persistentActor.tell("a", null); + persistentActor.tell("print", null); + persistentActor.tell("boom", null); + persistentActor.tell("print", null); + persistentActor.tell("b", null); + persistentActor.tell("print", null); + persistentActor.tell("c", null); + persistentActor.tell("print", null); + + // Will print in a first run (i.e. with empty journal): + + // received [a] + // received [a, b] + // received [a, b, c] + + // Will print in a second run: + + // received [a, b, c, a] + // received [a, b, c, a, b] + // received [a, b, c, a, b, c] + + // etc ... + + Thread.sleep(1000); + system.shutdown(); + } +} diff --git a/akka-samples/akka-sample-persistence-java-lambda/src/main/java/sample/persistence/ProcessorChannelExample.java b/akka-samples/akka-sample-persistence-java-lambda/src/main/java/sample/persistence/ProcessorChannelExample.java index 2acffd299f..bbca9bd5d8 100644 --- a/akka-samples/akka-sample-persistence-java-lambda/src/main/java/sample/persistence/ProcessorChannelExample.java +++ b/akka-samples/akka-sample-persistence-java-lambda/src/main/java/sample/persistence/ProcessorChannelExample.java @@ -14,11 +14,11 @@ import scala.PartialFunction; import scala.runtime.BoxedUnit; public class ProcessorChannelExample { - public static class ExampleProcessor extends AbstractProcessor { + public static class ExamplePersistentActor extends AbstractProcessor { private ActorRef destination; private ActorRef channel; - public ExampleProcessor(ActorRef destination) { + public ExamplePersistentActor(ActorRef destination) { this.destination = destination; this.channel = context().actorOf(Channel.props(), "channel"); @@ -47,7 +47,7 @@ public class ProcessorChannelExample { public static void main(String... args) throws Exception { final ActorSystem system = ActorSystem.create("example"); final ActorRef destination = system.actorOf(Props.create(ExampleDestination.class)); - final ActorRef processor = system.actorOf(Props.create(ExampleProcessor.class, destination), "processor-1"); + final ActorRef processor = system.actorOf(Props.create(ExamplePersistentActor.class, destination), "processor-1"); processor.tell(Persistent.create("a"), null); processor.tell(Persistent.create("b"), null); diff --git a/akka-samples/akka-sample-persistence-java-lambda/src/main/java/sample/persistence/ProcessorFailureExample.java b/akka-samples/akka-sample-persistence-java-lambda/src/main/java/sample/persistence/ProcessorFailureExample.java deleted file mode 100644 index 310fef14c4..0000000000 --- a/akka-samples/akka-sample-persistence-java-lambda/src/main/java/sample/persistence/ProcessorFailureExample.java +++ /dev/null @@ -1,71 +0,0 @@ -/** - * Copyright (C) 2009-2014 Typesafe Inc. - */ - -package sample.persistence; - -import akka.actor.ActorRef; -import akka.actor.ActorSystem; -import akka.actor.Props; -import akka.japi.pf.ReceiveBuilder; -import akka.persistence.AbstractProcessor; -import akka.persistence.Persistent; -import scala.Option; - -import java.util.ArrayList; - -public class ProcessorFailureExample { - public static class ExampleProcessor extends AbstractProcessor { - private ArrayList received = new ArrayList(); - - public ExampleProcessor() { - receive(ReceiveBuilder. - match(Persistent.class, p -> p.payload().equals("boom"), p -> {throw new RuntimeException("boom");}). - match(Persistent.class, p -> !p.payload().equals("boom"), p -> received.add(p.payload())). - match(String.class, s -> s.equals("boom"), s -> {throw new RuntimeException("boom");}). - match(String.class, s -> s.equals("print"), s -> System.out.println("received " + received)).build() - ); - } - - @Override - public void preRestart(Throwable reason, Option message) { - if (message.isDefined() && message.get() instanceof Persistent) { - deleteMessage(((Persistent) message.get()).sequenceNr(), false); - } - super.preRestart(reason, message); - } - } - - public static void main(String... args) throws Exception { - final ActorSystem system = ActorSystem.create("example"); - final ActorRef processor = system.actorOf(Props.create(ExampleProcessor.class), "processor-2"); - - processor.tell(Persistent.create("a"), null); - processor.tell("print", null); - processor.tell("boom", null); - processor.tell("print", null); - processor.tell(Persistent.create("b"), null); - processor.tell("print", null); - processor.tell(Persistent.create("boom"), null); - processor.tell("print", null); - processor.tell(Persistent.create("c"), null); - processor.tell("print", null); - - // Will print in a first run (i.e. with empty journal): - - // received [a] - // received [a, b] - // received [a, b, c] - - // Will print in a second run: - - // received [a, b, c, a] - // received [a, b, c, a, b] - // received [a, b, c, a, b, c] - - // etc ... - - Thread.sleep(1000); - system.shutdown(); - } -} diff --git a/akka-samples/akka-sample-persistence-java-lambda/src/main/java/sample/persistence/SnapshotExample.java b/akka-samples/akka-sample-persistence-java-lambda/src/main/java/sample/persistence/SnapshotExample.java index fda5598676..903ef982cd 100644 --- a/akka-samples/akka-sample-persistence-java-lambda/src/main/java/sample/persistence/SnapshotExample.java +++ b/akka-samples/akka-sample-persistence-java-lambda/src/main/java/sample/persistence/SnapshotExample.java @@ -8,7 +8,7 @@ import akka.actor.ActorRef; import akka.actor.ActorSystem; import akka.actor.Props; import akka.japi.pf.ReceiveBuilder; -import akka.persistence.AbstractProcessor; +import akka.persistence.AbstractPersistentActor; import akka.persistence.Persistent; import akka.persistence.SnapshotOffer; import scala.PartialFunction; @@ -43,36 +43,47 @@ public class SnapshotExample { } } - public static class ExampleProcessor extends AbstractProcessor { + public static class ExamplePersistentActor extends AbstractPersistentActor { private ExampleState state = new ExampleState(); - public ExampleProcessor() { - receive(ReceiveBuilder. - match(Persistent.class, p -> state.update(String.format("%s-%d", p.payload(), p.sequenceNr()))). - match(SnapshotOffer.class, s -> { - ExampleState exState = (ExampleState) s.snapshot(); - System.out.println("offered state = " + exState); - state = exState; - }). + @Override + public PartialFunction receiveCommand() { + return ReceiveBuilder. match(String.class, s -> s.equals("print"), s -> System.out.println("current state = " + state)). match(String.class, s -> s.equals("snap"), s -> // IMPORTANT: create a copy of snapshot // because ExampleState is mutable !!! - saveSnapshot(state.copy())).build() - ); + saveSnapshot(state.copy())). + match(String.class, s -> { + persist(s, evt -> { + state.update(evt); + }); + }). + build(); + } + + @Override + public PartialFunction receiveRecover() { + return ReceiveBuilder. + match(String.class, evt -> state.update(evt)). + match(SnapshotOffer.class, ss -> { + System.out.println("offered state = " + ss); + state = (ExampleState) ss.snapshot(); + }). + build(); } } public static void main(String... args) throws Exception { final ActorSystem system = ActorSystem.create("example"); - final ActorRef processor = system.actorOf(Props.create(ExampleProcessor.class), "processor-3-java"); + final ActorRef persistentActor = system.actorOf(Props.create(ExamplePersistentActor.class), "persistentActor-3-java"); - processor.tell(Persistent.create("a"), null); - processor.tell(Persistent.create("b"), null); - processor.tell("snap", null); - processor.tell(Persistent.create("c"), null); - processor.tell(Persistent.create("d"), null); - processor.tell("print", null); + persistentActor.tell("a", null); + persistentActor.tell("b", null); + persistentActor.tell("snap", null); + persistentActor.tell("c", null); + persistentActor.tell("d", null); + persistentActor.tell("print", null); Thread.sleep(1000); system.shutdown(); diff --git a/akka-samples/akka-sample-persistence-java-lambda/src/main/java/sample/persistence/ViewExample.java b/akka-samples/akka-sample-persistence-java-lambda/src/main/java/sample/persistence/ViewExample.java index 2fe074111e..39f9273c83 100644 --- a/akka-samples/akka-sample-persistence-java-lambda/src/main/java/sample/persistence/ViewExample.java +++ b/akka-samples/akka-sample-persistence-java-lambda/src/main/java/sample/persistence/ViewExample.java @@ -17,25 +17,35 @@ import scala.runtime.BoxedUnit; import java.util.concurrent.TimeUnit; public class ViewExample { - public static class ExampleProcessor extends AbstractProcessor { + public static class ExamplePersistentActor extends AbstractPersistentActor { + private int count = 1; + @Override - public String processorId() { - return "processor-5"; + public String persistenceId() { + return "persistentActor-5"; } - public ExampleProcessor() { - receive(ReceiveBuilder. - match(Persistent.class, - p -> System.out.println(String.format("processor received %s (sequence nr = %d)", - p.payload(), - p.sequenceNr()))).build() - ); + @Override + public PartialFunction receiveCommand() { + return ReceiveBuilder. + match(String.class, s -> { + System.out.println(String.format("persistentActor received %s (nr = %d)", s, count)); + persist(s + count, evt -> { + count += 1; + }); + }). + build(); + } + + @Override + public PartialFunction receiveRecover() { + return ReceiveBuilder. + match(String.class, s -> count += 1). + build(); } } public static class ExampleView extends AbstractView { - private final ActorRef destination = context().actorOf(Props.create(ExampleDestination.class)); - private final ActorRef channel = context().actorOf(Channel.props("channel")); private int numReplicated = 0; @@ -46,19 +56,16 @@ public class ViewExample { @Override public String persistenceId() { - return "processor-5"; + return "persistentActor-5"; } public ExampleView() { receive(ReceiveBuilder. match(Persistent.class, p -> { numReplicated += 1; - System.out.println(String.format("view received %s (sequence nr = %d, num replicated = %d)", + System.out.println(String.format("view received %s (num replicated = %d)", p.payload(), - p.sequenceNr(), numReplicated)); - channel.tell(Deliver.create(p.withPayload("replicated-" + p.payload()), destination.path()), - self()); }). match(SnapshotOffer.class, so -> { numReplicated = (Integer) so.snapshot(); @@ -71,30 +78,16 @@ public class ViewExample { } } - public static class ExampleDestination extends AbstractActor { - - public ExampleDestination() { - receive(ReceiveBuilder. - match(ConfirmablePersistent.class, cp -> { - System.out.println(String.format("destination received %s (sequence nr = %s)", - cp.payload(), - cp.sequenceNr())); - cp.confirm(); - }).build() - ); - } - } - public static void main(String... args) throws Exception { final ActorSystem system = ActorSystem.create("example"); - final ActorRef processor = system.actorOf(Props.create(ExampleProcessor.class)); + final ActorRef persistentActor = system.actorOf(Props.create(ExamplePersistentActor.class)); final ActorRef view = system.actorOf(Props.create(ExampleView.class)); system.scheduler() .schedule(Duration.Zero(), Duration.create(2, TimeUnit.SECONDS), - processor, - Persistent.create("scheduled"), + persistentActor, + "scheduled", system.dispatcher(), null); system.scheduler() diff --git a/akka-samples/akka-sample-persistence-java-lambda/tutorial/index.html b/akka-samples/akka-sample-persistence-java-lambda/tutorial/index.html index 4ad57d4772..f94463f3da 100644 --- a/akka-samples/akka-sample-persistence-java-lambda/tutorial/index.html +++ b/akka-samples/akka-sample-persistence-java-lambda/tutorial/index.html @@ -12,12 +12,10 @@ This tutorial contains examples that illustrate a subset of Akka Persistence features.

    -
  • Processors and channels
  • -
  • Processsor snapshots
  • -
  • Eventsourced processors
  • -
  • Processor failure handling
  • -
  • Processor views
  • -
  • Processor conversation recovery
  • +
  • persistent actor
  • +
  • persistent actor snapshots
  • +
  • persistent actor recovery
  • +
  • persistent actor views

@@ -27,47 +25,13 @@ Custom storage locations for the journal and snapshots can be defined in

-

Processors and channels

+

Persistent actor

-ProcessorChannelExample.java -defines an ExampleProcessor and an ExampleDestination. The processor sends messages to a -destination via a channel. The destination confirms the delivery of these messages so that replayed messages aren't -redundantly delivered to the destination. Repeated runs of the application demonstrates that the processor receives -both replayed and new messages whereas the channel only receives new messages, sent by the application. The processor -also receives replies from the destination, demonstrating that a channel preserves sender references. -

- -

-To run this example, go to the Run tab, and run the application main class -sample.persistence.ProcessorChannelExample several times. -

-
- -
-

Processor snapshots

-

-SnapshotExample.java -demonstrates how processors can take snapshots of application state and recover from previously stored snapshots. -Snapshots are offered to processors at the beginning of recovery, before any messages (younger than the snapshot) -are replayed. -

- -

-To run this example, go to the Run tab, and run the application main class -sample.persistence.SnapshotExample several times. With every run, the state offered by the -most recent snapshot is printed to stdout, followed by the updated state after sending new persistent -messages to the processor. -

-
- -
-

Eventsourced processors

-

-EventsourcedExample.java -is described in detail in the Event sourcing -section of the user documentation. With every application run, the ExampleProcessor is recovered from +PersistentActorExample.java +is described in detail in the Event sourcing +section of the user documentation. With every application run, the ExamplePersistentActor is recovered from events stored in previous application runs, processes new commands, stores new events and snapshots and prints the -current processor state to stdout. +current persistent actor state to stdout.

@@ -77,60 +41,48 @@ To run this example, go to the Run tab, and

-

Processor failure handling

+

Persistent actor snapshots

-ProcessorFailureExample.java -shows how a processor can delete persistent messages from the journal if they threw an exception. Throwing an exception -restarts the processor and replays messages. In order to prevent that the message that caused the exception is replayed, -it is marked as deleted in the journal (during invocation of preRestart). This is a common pattern in -command-sourcing to compensate write-ahead logging of messages. +SnapshotExample.java +demonstrates how persistent actors can take snapshots of application state and recover from previously stored snapshots. +Snapshots are offered to persistent actors at the beginning of recovery, before any messages (younger than the snapshot) +are replayed.

To run this example, go to the Run tab, and run the application main class -sample.persistence.ProcessorFailureExample several times. -

- -

-Event sourcing -on the other hand, does not persist commands directly but rather events that have been derived from received commands -(not shown here). These events are known to be successfully applicable to current processor state i.e. there's -no need for deleting them from the journal. Event sourced processors usually have a lower throughput than command -sourced processors, as the maximum size of a write batch is limited by the number of persisted events per received -command. +sample.persistence.SnapshotExample several times. With every run, the state offered by the +most recent snapshot is printed to stdout, followed by the updated state after sending new persistent +messages to the persistent actor.

-

Processor views

+

Persistent actor recovery

+

+PersistentActorFailureExample.java +shows how a persistent actor can throw an exception, restart and restore the state by replaying the events. +

+ +

+To run this example, go to the Run tab, and run the application main class +sample.persistence.PersistentActorFailureExample several times. +

+
+ +
+

Persistent actor views

ViewExample.java demonstrates -how a view (ExampleView) is updated with the persistent message stream of a processor -(ExampleProcessor). Messages sent to the processor are read from stdin. Views also support -snapshotting and can be used in combination with channels in the same way as processors. +how a view (ExampleView) is updated with the persistent message stream of a persistent actor +(ExamplePersistentActor). Messages sent to the persistent actor are scheduled periodically. Views also support +snapshotting to reduce recovery time.

To run this example, go to the Run tab, and run the application main class sample.persistence.ViewExample.

- -

-Views can also receive events that have been persisted by event sourced processors (not shown). -

-
- -
-

Processor conversation recovery

-

-ConversationRecoveryExample.java -defines two processors that send messages to each other via channels. The reliable delivery properties of channels, -in combination with processors, allow these processors to automatically resume their conversation after a JVM crash. -

-

-To run this example, go to the Run tab, and run the application main class -sample.persistence.ConversationRecoveryExample several times. -

diff --git a/akka-samples/akka-sample-persistence-java/src/main/java/sample/persistence/ConversationRecoveryExample.java b/akka-samples/akka-sample-persistence-java/src/main/java/sample/persistence/ConversationRecoveryExample.java deleted file mode 100644 index e7c96e9477..0000000000 --- a/akka-samples/akka-sample-persistence-java/src/main/java/sample/persistence/ConversationRecoveryExample.java +++ /dev/null @@ -1,58 +0,0 @@ -package sample.persistence; - -import akka.actor.*; -import akka.persistence.*; - -public class ConversationRecoveryExample { - public static String PING = "PING"; - public static String PONG = "PONG"; - - public static class Ping extends UntypedProcessor { - private final ActorRef pongChannel = getContext().actorOf(Channel.props(), "pongChannel"); - private int counter = 0; - - @Override - public void onReceive(Object message) throws Exception { - if (message instanceof ConfirmablePersistent) { - ConfirmablePersistent msg = (ConfirmablePersistent)message; - if (msg.payload().equals(PING)) { - counter += 1; - System.out.println(String.format("received ping %d times", counter)); - msg.confirm(); - if (!recoveryRunning()) Thread.sleep(1000); - pongChannel.tell(Deliver.create(msg.withPayload(PONG), getSender().path()), getSelf()); - } - } else if (message.equals("init") && counter == 0) { - pongChannel.tell(Deliver.create(Persistent.create(PONG), getSender().path()), getSelf()); - } - } - } - - public static class Pong extends UntypedProcessor { - private final ActorRef pingChannel = getContext().actorOf(Channel.props(), "pingChannel"); - private int counter = 0; - - @Override - public void onReceive(Object message) throws Exception { - if (message instanceof ConfirmablePersistent) { - ConfirmablePersistent msg = (ConfirmablePersistent)message; - if (msg.payload().equals(PONG)) { - counter += 1; - System.out.println(String.format("received pong %d times", counter)); - msg.confirm(); - if (!recoveryRunning()) Thread.sleep(1000); - pingChannel.tell(Deliver.create(msg.withPayload(PING), getSender().path()), getSelf()); - } - } - } - } - - public static void main(String... args) throws Exception { - final ActorSystem system = ActorSystem.create("example"); - - final ActorRef ping = system.actorOf(Props.create(Ping.class), "ping"); - final ActorRef pong = system.actorOf(Props.create(Pong.class), "pong"); - - ping.tell("init", pong); - } -} diff --git a/akka-samples/akka-sample-persistence-java/src/main/java/sample/persistence/PersistentActorExample.java b/akka-samples/akka-sample-persistence-java/src/main/java/sample/persistence/PersistentActorExample.java index 0a279debaf..38247d2d26 100644 --- a/akka-samples/akka-sample-persistence-java/src/main/java/sample/persistence/PersistentActorExample.java +++ b/akka-samples/akka-sample-persistence-java/src/main/java/sample/persistence/PersistentActorExample.java @@ -10,7 +10,6 @@ import akka.persistence.UntypedPersistentActor; import java.io.Serializable; import java.util.ArrayList; - import static java.util.Arrays.asList; class Cmd implements Serializable { @@ -66,21 +65,25 @@ class ExampleState implements Serializable { } } -class ExampleProcessor extends UntypedPersistentActor { +class ExamplePersistentActor extends UntypedPersistentActor { private ExampleState state = new ExampleState(); public int getNumEvents() { return state.size(); } + @Override public void onReceiveRecover(Object msg) { if (msg instanceof Evt) { state.update((Evt) msg); } else if (msg instanceof SnapshotOffer) { state = (ExampleState)((SnapshotOffer)msg).snapshot(); + } else { + unhandled(msg); } } + @Override public void onReceiveCommand(Object msg) { if (msg instanceof Cmd) { final String data = ((Cmd)msg).getData(); @@ -100,6 +103,8 @@ class ExampleProcessor extends UntypedPersistentActor { saveSnapshot(state.copy()); } else if (msg.equals("print")) { System.out.println(state); + } else { + unhandled(msg); } } } @@ -108,14 +113,15 @@ class ExampleProcessor extends UntypedPersistentActor { public class PersistentActorExample { public static void main(String... args) throws Exception { final ActorSystem system = ActorSystem.create("example"); - final ActorRef processor = system.actorOf(Props.create(ExampleProcessor.class), "processor-4-java"); + final ActorRef persistentActor = + system.actorOf(Props.create(ExamplePersistentActor.class), "persistentActor-4-java"); - processor.tell(new Cmd("foo"), null); - processor.tell(new Cmd("baz"), null); - processor.tell(new Cmd("bar"), null); - processor.tell("snap", null); - processor.tell(new Cmd("buzz"), null); - processor.tell("print", null); + persistentActor.tell(new Cmd("foo"), null); + persistentActor.tell(new Cmd("baz"), null); + persistentActor.tell(new Cmd("bar"), null); + persistentActor.tell("snap", null); + persistentActor.tell(new Cmd("buzz"), null); + persistentActor.tell("print", null); Thread.sleep(1000); system.shutdown(); diff --git a/akka-samples/akka-sample-persistence-java/src/main/java/sample/persistence/PersistentActorFailureExample.java b/akka-samples/akka-sample-persistence-java/src/main/java/sample/persistence/PersistentActorFailureExample.java new file mode 100644 index 0000000000..277a7b3806 --- /dev/null +++ b/akka-samples/akka-sample-persistence-java/src/main/java/sample/persistence/PersistentActorFailureExample.java @@ -0,0 +1,71 @@ +package sample.persistence; + +import java.util.ArrayList; +import akka.japi.Procedure; +import akka.actor.*; +import akka.persistence.*; + +public class PersistentActorFailureExample { + public static class ExamplePersistentActor extends UntypedPersistentActor { + private ArrayList received = new ArrayList(); + + @Override + public void onReceiveCommand(Object message) throws Exception { + if (message.equals("boom")) { + throw new Exception("boom"); + } else if (message.equals("print")) { + System.out.println("received " + received); + } else if (message instanceof String) { + String s = (String) message; + persist(s, new Procedure() { + public void apply(String evt) throws Exception { + received.add(evt); + } + }); + } else { + unhandled(message); + } + } + + @Override + public void onReceiveRecover(Object message) { + if (message instanceof String) { + received.add((String) message); + } else { + unhandled(message); + } + } + + } + + public static void main(String... args) throws Exception { + final ActorSystem system = ActorSystem.create("example"); + final ActorRef persistentActor = system.actorOf(Props.create(ExamplePersistentActor.class), "persistentActor-2"); + + persistentActor.tell("a", null); + persistentActor.tell("print", null); + persistentActor.tell("boom", null); + persistentActor.tell("print", null); + persistentActor.tell("b", null); + persistentActor.tell("print", null); + persistentActor.tell("c", null); + persistentActor.tell("print", null); + + // Will print in a first run (i.e. with empty journal): + + // received [a] + // received [a, b] + // received [a, b, c] + + // Will print in a second run: + + // received [a, b, c, a] + // received [a, b, c, a, b] + // received [a, b, c, a, b, c] + + // etc ... + + Thread.sleep(1000); + system.shutdown(); + } +} diff --git a/akka-samples/akka-sample-persistence-java/src/main/java/sample/persistence/ProcessorChannelExample.java b/akka-samples/akka-sample-persistence-java/src/main/java/sample/persistence/ProcessorChannelExample.java deleted file mode 100644 index ded08da548..0000000000 --- a/akka-samples/akka-sample-persistence-java/src/main/java/sample/persistence/ProcessorChannelExample.java +++ /dev/null @@ -1,51 +0,0 @@ -package sample.persistence; - -import akka.actor.*; -import akka.persistence.*; - -public class ProcessorChannelExample { - public static class ExampleProcessor extends UntypedProcessor { - private ActorRef destination; - private ActorRef channel; - - public ExampleProcessor(ActorRef destination) { - this.destination = destination; - this.channel = getContext().actorOf(Channel.props(), "channel"); - } - - @Override - public void onReceive(Object message) throws Exception { - if (message instanceof Persistent) { - Persistent msg = (Persistent)message; - System.out.println("processed " + msg.payload()); - channel.tell(Deliver.create(msg.withPayload("processed " + msg.payload()), destination.path()), getSelf()); - } else if (message instanceof String) { - System.out.println("reply = " + message); - } - } - } - - public static class ExampleDestination extends UntypedActor { - @Override - public void onReceive(Object message) throws Exception { - if (message instanceof ConfirmablePersistent) { - ConfirmablePersistent msg = (ConfirmablePersistent)message; - System.out.println("received " + msg.payload()); - getSender().tell(String.format("re: %s (%d)", msg.payload(), msg.sequenceNr()), null); - msg.confirm(); - } - } - } - - public static void main(String... args) throws Exception { - final ActorSystem system = ActorSystem.create("example"); - final ActorRef destination = system.actorOf(Props.create(ExampleDestination.class)); - final ActorRef processor = system.actorOf(Props.create(ExampleProcessor.class, destination), "processor-1"); - - processor.tell(Persistent.create("a"), null); - processor.tell(Persistent.create("b"), null); - - Thread.sleep(1000); - system.shutdown(); - } -} diff --git a/akka-samples/akka-sample-persistence-java/src/main/java/sample/persistence/ProcessorFailureExample.java b/akka-samples/akka-sample-persistence-java/src/main/java/sample/persistence/ProcessorFailureExample.java deleted file mode 100644 index 743680e174..0000000000 --- a/akka-samples/akka-sample-persistence-java/src/main/java/sample/persistence/ProcessorFailureExample.java +++ /dev/null @@ -1,71 +0,0 @@ -package sample.persistence; - -import java.util.ArrayList; - -import scala.Option; - -import akka.actor.*; -import akka.persistence.*; - -public class ProcessorFailureExample { - public static class ExampleProcessor extends UntypedProcessor { - private ArrayList received = new ArrayList(); - - @Override - public void onReceive(Object message) throws Exception { - if (message instanceof Persistent) { - Persistent persistent = (Persistent)message; - if (persistent.payload() == "boom") { - throw new Exception("boom"); - } else { - received.add(persistent.payload()); - } - } else if (message == "boom") { - throw new Exception("boom"); - } else if (message == "print") { - System.out.println("received " + received); - } - } - - @Override - public void preRestart(Throwable reason, Option message) { - if (message.isDefined() && message.get() instanceof Persistent) { - deleteMessage(((Persistent) message.get()).sequenceNr(), false); - } - super.preRestart(reason, message); - } - } - - public static void main(String... args) throws Exception { - final ActorSystem system = ActorSystem.create("example"); - final ActorRef processor = system.actorOf(Props.create(ExampleProcessor.class), "processor-2"); - - processor.tell(Persistent.create("a"), null); - processor.tell("print", null); - processor.tell("boom", null); - processor.tell("print", null); - processor.tell(Persistent.create("b"), null); - processor.tell("print", null); - processor.tell(Persistent.create("boom"), null); - processor.tell("print", null); - processor.tell(Persistent.create("c"), null); - processor.tell("print", null); - - // Will print in a first run (i.e. with empty journal): - - // received [a] - // received [a, b] - // received [a, b, c] - - // Will print in a second run: - - // received [a, b, c, a] - // received [a, b, c, a, b] - // received [a, b, c, a, b, c] - - // etc ... - - Thread.sleep(1000); - system.shutdown(); - } -} diff --git a/akka-samples/akka-sample-persistence-java/src/main/java/sample/persistence/SnapshotExample.java b/akka-samples/akka-sample-persistence-java/src/main/java/sample/persistence/SnapshotExample.java index 223b138698..8407685981 100644 --- a/akka-samples/akka-sample-persistence-java/src/main/java/sample/persistence/SnapshotExample.java +++ b/akka-samples/akka-sample-persistence-java/src/main/java/sample/persistence/SnapshotExample.java @@ -5,6 +5,7 @@ import java.util.ArrayList; import akka.actor.*; import akka.persistence.*; +import akka.japi.Procedure; public class SnapshotExample { public static class ExampleState implements Serializable { @@ -32,38 +33,57 @@ public class SnapshotExample { } } - public static class ExampleProcessor extends UntypedProcessor { + public static class ExamplePersistentActor extends UntypedPersistentActor { private ExampleState state = new ExampleState(); @Override - public void onReceive(Object message) throws Exception { - if (message instanceof Persistent) { - Persistent persistent = (Persistent)message; - state.update(String.format("%s-%d", persistent.payload(), persistent.sequenceNr())); - } else if (message instanceof SnapshotOffer) { - ExampleState s = (ExampleState)((SnapshotOffer)message).snapshot(); - System.out.println("offered state = " + s); - state = s; - } else if (message.equals("print")) { + public void onReceiveCommand(Object message) { + if (message.equals("print")) { System.out.println("current state = " + state); } else if (message.equals("snap")) { // IMPORTANT: create a copy of snapshot // because ExampleState is mutable !!! saveSnapshot(state.copy()); + } else if (message instanceof SaveSnapshotSuccess) { + // ... + } else if (message instanceof SaveSnapshotFailure) { + // ... + } else if (message instanceof String) { + String s = (String) message; + persist(s, new Procedure() { + public void apply(String evt) throws Exception { + state.update(evt); + } + }); + } else { + unhandled(message); } } + + @Override + public void onReceiveRecover(Object message) { + if (message instanceof SnapshotOffer) { + ExampleState s = (ExampleState)((SnapshotOffer)message).snapshot(); + System.out.println("offered state = " + s); + state = s; + } else if (message instanceof String) { + state.update((String) message); + } else { + unhandled(message); + } + } } public static void main(String... args) throws Exception { final ActorSystem system = ActorSystem.create("example"); - final ActorRef processor = system.actorOf(Props.create(ExampleProcessor.class), "processor-3-java"); + final ActorRef persistentActor = system.actorOf(Props.create(ExamplePersistentActor.class), "persistentActor-3-java"); - processor.tell(Persistent.create("a"), null); - processor.tell(Persistent.create("b"), null); - processor.tell("snap", null); - processor.tell(Persistent.create("c"), null); - processor.tell(Persistent.create("d"), null); - processor.tell("print", null); + persistentActor.tell("a", null); + persistentActor.tell("b", null); + persistentActor.tell("snap", null); + persistentActor.tell("c", null); + persistentActor.tell("d", null); + persistentActor.tell("print", null); Thread.sleep(1000); system.shutdown(); diff --git a/akka-samples/akka-sample-persistence-java/src/main/java/sample/persistence/ViewExample.java b/akka-samples/akka-sample-persistence-java/src/main/java/sample/persistence/ViewExample.java index b132a5d652..d22b6634d3 100644 --- a/akka-samples/akka-sample-persistence-java/src/main/java/sample/persistence/ViewExample.java +++ b/akka-samples/akka-sample-persistence-java/src/main/java/sample/persistence/ViewExample.java @@ -1,31 +1,47 @@ package sample.persistence; import java.util.concurrent.TimeUnit; - import scala.concurrent.duration.Duration; import akka.actor.*; import akka.persistence.*; +import akka.japi.Procedure; public class ViewExample { - public static class ExampleProcessor extends UntypedProcessor { + public static class ExamplePersistentActor extends UntypedPersistentActor { + private int count = 1; + @Override - public String processorId() { - return "processor-5"; + public String persistenceId() { + return "persistentActor-5"; + } + + @Override + public void onReceiveRecover(Object message) { + if (message instanceof String) { + count += 1; + } else { + unhandled(message); + } } @Override - public void onReceive(Object message) throws Exception { - if (message instanceof Persistent) { - Persistent p = (Persistent)message; - System.out.println(String.format("processor received %s (sequence nr = %d)", p.payload(), p.sequenceNr())); - } + public void onReceiveCommand(Object message) { + if (message instanceof String) { + String s = (String) message; + System.out.println(String.format("persistentActor received %s (nr = %d)", s, count)); + persist(s + count, new Procedure() { + public void apply(String evt) { + count += 1; + } + }); + } else { + unhandled(message); + } } } public static class ExampleView extends UntypedView { - private final ActorRef destination = getContext().actorOf(Props.create(ExampleDestination.class)); - private final ActorRef channel = getContext().actorOf(Channel.props("channel")); private int numReplicated = 0; @@ -36,7 +52,7 @@ public class ViewExample { @Override public String persistenceId() { - return "processor-5"; + return "persistentActor-5"; } @Override @@ -44,8 +60,7 @@ public class ViewExample { if (message instanceof Persistent) { Persistent p = (Persistent)message; numReplicated += 1; - System.out.println(String.format("view received %s (sequence nr = %d, num replicated = %d)", p.payload(), p.sequenceNr(), numReplicated)); - channel.tell(Deliver.create(p.withPayload("replicated-" + p.payload()), destination.path()), getSelf()); + System.out.println(String.format("view received %s (num replicated = %d)", p.payload(), numReplicated)); } else if (message instanceof SnapshotOffer) { SnapshotOffer so = (SnapshotOffer)message; numReplicated = (Integer)so.snapshot(); @@ -56,23 +71,12 @@ public class ViewExample { } } - public static class ExampleDestination extends UntypedActor { - @Override - public void onReceive(Object message) throws Exception { - if (message instanceof ConfirmablePersistent) { - ConfirmablePersistent cp = (ConfirmablePersistent)message; - System.out.println(String.format("destination received %s (sequence nr = %s)", cp.payload(), cp.sequenceNr())); - cp.confirm(); - } - } - } - public static void main(String... args) throws Exception { final ActorSystem system = ActorSystem.create("example"); - final ActorRef processor = system.actorOf(Props.create(ExampleProcessor.class)); + final ActorRef persistentActor = system.actorOf(Props.create(ExamplePersistentActor.class)); final ActorRef view = system.actorOf(Props.create(ExampleView.class)); - system.scheduler().schedule(Duration.Zero(), Duration.create(2, TimeUnit.SECONDS), processor, Persistent.create("scheduled"), system.dispatcher(), null); + system.scheduler().schedule(Duration.Zero(), Duration.create(2, TimeUnit.SECONDS), persistentActor, "scheduled", system.dispatcher(), null); system.scheduler().schedule(Duration.Zero(), Duration.create(5, TimeUnit.SECONDS), view, "snap", system.dispatcher(), null); } } diff --git a/akka-samples/akka-sample-persistence-java/tutorial/index.html b/akka-samples/akka-sample-persistence-java/tutorial/index.html index 67b02252f1..8431c352df 100644 --- a/akka-samples/akka-sample-persistence-java/tutorial/index.html +++ b/akka-samples/akka-sample-persistence-java/tutorial/index.html @@ -12,12 +12,10 @@ This tutorial contains examples that illustrate a subset of Akka Persistence features.

    -
  • Processors and channels
  • -
  • Processsor snapshots
  • -
  • Eventsourced processors
  • -
  • Processor failure handling
  • -
  • Processor views
  • -
  • Processor conversation recovery
  • +
  • persistent actor
  • +
  • persistent actor snapshots
  • +
  • persistent actor recovery
  • +
  • persistent actor views

@@ -27,47 +25,13 @@ Custom storage locations for the journal and snapshots can be defined in

-

Processors and channels

+

Persistent actor

-ProcessorChannelExample.java -defines an ExampleProcessor and an ExampleDestination. The processor sends messages to a -destination via a channel. The destination confirms the delivery of these messages so that replayed messages aren't -redundantly delivered to the destination. Repeated runs of the application demonstrates that the processor receives -both replayed and new messages whereas the channel only receives new messages, sent by the application. The processor -also receives replies from the destination, demonstrating that a channel preserves sender references. -

- -

-To run this example, go to the Run tab, and run the application main class -sample.persistence.ProcessorChannelExample several times. -

-
- -
-

Processor snapshots

-

-SnapshotExample.java -demonstrates how processors can take snapshots of application state and recover from previously stored snapshots. -Snapshots are offered to processors at the beginning of recovery, before any messages (younger than the snapshot) -are replayed. -

- -

-To run this example, go to the Run tab, and run the application main class -sample.persistence.SnapshotExample several times. With every run, the state offered by the -most recent snapshot is printed to stdout, followed by the updated state after sending new persistent -messages to the processor. -

-
- -
-

Eventsourced processors

-

-EventsourcedExample.java -is described in detail in the Event sourcing -section of the user documentation. With every application run, the ExampleProcessor is recovered from +PersistentActorExample.java +is described in detail in the Event sourcing +section of the user documentation. With every application run, the ExamplePersistentActor is recovered from events stored in previous application runs, processes new commands, stores new events and snapshots and prints the -current processor state to stdout. +current persistent actor state to stdout.

@@ -77,7 +41,37 @@ To run this example, go to the Run tab, and

-

Processor failure handling

+

Persistent actor snapshots

+

+SnapshotExample.java +demonstrates how persistent actors can take snapshots of application state and recover from previously stored snapshots. +Snapshots are offered to persistent actors at the beginning of recovery, before any messages (younger than the snapshot) +are replayed. +

+ +

+To run this example, go to the Run tab, and run the application main class +sample.persistence.SnapshotExample several times. With every run, the state offered by the +most recent snapshot is printed to stdout, followed by the updated state after sending new persistent +messages to the persistent actor. +

+
+ +
+

Persistent actor recovery

+

+PersistentActorFailureExample.java +shows how a persistent actor can throw an exception, restart and restore the state by replaying the events. +

+ +

+To run this example, go to the Run tab, and run the application main class +sample.persistence.PersistentActorFailureExample several times. +

+
+ +
+

Persistent actor views

ProcessorFailureExample.java shows how a processor can delete persistent messages from the journal if they threw an exception. Throwing an exception @@ -105,32 +99,15 @@ command.

Processor views

ViewExample.java demonstrates -how a view (ExampleView) is updated with the persistent message stream of a processor -(ExampleProcessor). Messages sent to the processor are read from stdin. Views also support -snapshotting and can be used in combination with channels in the same way as processors. +how a view (ExampleView) is updated with the persistent message stream of a persistent actor +(ExamplePersistentActor). Messages sent to the persistent actor are scheduled periodically. Views also support +snapshotting to reduce recovery time.

To run this example, go to the Run tab, and run the application main class sample.persistence.ViewExample.

- -

-Views can also receive events that have been persisted by event sourced processors (not shown). -

-
- -
-

Processor conversation recovery

-

-ConversationRecoveryExample.java -defines two processors that send messages to each other via channels. The reliable delivery properties of channels, -in combination with processors, allow these processors to automatically resume their conversation after a JVM crash. -

-

-To run this example, go to the Run tab, and run the application main class -sample.persistence.ConversationRecoveryExample several times. -

diff --git a/akka-samples/akka-sample-persistence-scala/build.sbt b/akka-samples/akka-sample-persistence-scala/build.sbt index 3d2d9e3ee1..ebc68c6f94 100644 --- a/akka-samples/akka-sample-persistence-scala/build.sbt +++ b/akka-samples/akka-sample-persistence-scala/build.sbt @@ -5,6 +5,7 @@ version := "2.4-SNAPSHOT" scalaVersion := "2.10.4" libraryDependencies ++= Seq( - "com.typesafe.akka" %% "akka-persistence-experimental" % "2.4-SNAPSHOT" + "com.typesafe.akka" %% "akka-persistence-experimental" % "2.4-SNAPSHOT", + "com.typesafe.akka" %% "akka-stream-experimental" % "0.3" ) diff --git a/akka-samples/akka-sample-persistence-scala/src/main/scala/sample/persistence/ConversationRecoveryExample.scala b/akka-samples/akka-sample-persistence-scala/src/main/scala/sample/persistence/ConversationRecoveryExample.scala deleted file mode 100644 index 434ff57206..0000000000 --- a/akka-samples/akka-sample-persistence-scala/src/main/scala/sample/persistence/ConversationRecoveryExample.scala +++ /dev/null @@ -1,46 +0,0 @@ -package sample.persistence - -import akka.actor._ -import akka.persistence._ - -object ConversationRecoveryExample extends App { - case object Ping - case object Pong - - class Ping extends Processor { - val pongChannel = context.actorOf(Channel.props, "pongChannel") - var counter = 0 - - def receive = { - case m @ ConfirmablePersistent(Ping, _, _) => - counter += 1 - println(s"received ping ${counter} times ...") - m.confirm() - if (!recoveryRunning) Thread.sleep(1000) - pongChannel ! Deliver(m.withPayload(Pong), sender().path) - case "init" if (counter == 0) => - pongChannel ! Deliver(Persistent(Pong), sender().path) - } - } - - class Pong extends Processor { - val pingChannel = context.actorOf(Channel.props, "pingChannel") - var counter = 0 - - def receive = { - case m @ ConfirmablePersistent(Pong, _, _) => - counter += 1 - println(s"received pong ${counter} times ...") - m.confirm() - if (!recoveryRunning) Thread.sleep(1000) - pingChannel ! Deliver(m.withPayload(Ping), sender().path) - } - } - - val system = ActorSystem("example") - - val ping = system.actorOf(Props(classOf[Ping]), "ping") - val pong = system.actorOf(Props(classOf[Pong]), "pong") - - ping tell ("init", pong) -} diff --git a/akka-samples/akka-sample-persistence-scala/src/main/scala/sample/persistence/PersistentActorFailureExample.scala b/akka-samples/akka-sample-persistence-scala/src/main/scala/sample/persistence/PersistentActorFailureExample.scala new file mode 100644 index 0000000000..6341d3d9a2 --- /dev/null +++ b/akka-samples/akka-sample-persistence-scala/src/main/scala/sample/persistence/PersistentActorFailureExample.scala @@ -0,0 +1,51 @@ +package sample.persistence + +import akka.actor._ +import akka.persistence._ + +object PersistentActorFailureExample extends App { + class ExamplePersistentActor extends PersistentActor { + var received: List[String] = Nil // state + + def receiveCommand: Actor.Receive = { + case "print" => println(s"received ${received.reverse}") + case "boom" => throw new Exception("boom") + case payload: String => + persist(payload) { p => received = p :: received } + + } + + def receiveRecover: Actor.Receive = { + case s: String => received = s :: received + } + } + + val system = ActorSystem("example") + val persistentActor = system.actorOf(Props(classOf[ExamplePersistentActor]), "persistentActor-2") + + persistentActor ! "a" + persistentActor ! "print" + persistentActor ! "boom" // restart and recovery + persistentActor ! "print" + persistentActor ! "b" + persistentActor ! "print" + persistentActor ! "c" + persistentActor ! "print" + + // Will print in a first run (i.e. with empty journal): + + // received List(a) + // received List(a, b) + // received List(a, b, c) + + // Will print in a second run: + + // received List(a, b, c, a) + // received List(a, b, c, a, b) + // received List(a, b, c, a, b, c) + + // etc ... + + Thread.sleep(1000) + system.shutdown() +} diff --git a/akka-samples/akka-sample-persistence-scala/src/main/scala/sample/persistence/ProcessorChannelExample.scala b/akka-samples/akka-sample-persistence-scala/src/main/scala/sample/persistence/ProcessorChannelExample.scala deleted file mode 100644 index 716aa23c4c..0000000000 --- a/akka-samples/akka-sample-persistence-scala/src/main/scala/sample/persistence/ProcessorChannelExample.scala +++ /dev/null @@ -1,37 +0,0 @@ -package sample.persistence - -import akka.actor._ -import akka.persistence._ - -object ProcessorChannelExample extends App { - class ExampleProcessor extends Processor { - val channel = context.actorOf(Channel.props, "channel") - val destination = context.actorOf(Props[ExampleDestination]) - - def receive = { - case p @ Persistent(payload, _) => - println(s"processed ${payload}") - channel ! Deliver(p.withPayload(s"processed ${payload}"), destination.path) - case reply: String => - println(s"reply = ${reply}") - } - } - - class ExampleDestination extends Actor { - def receive = { - case p @ ConfirmablePersistent(payload, snr, _) => - println(s"received ${payload}") - sender() ! s"re: ${payload} (${snr})" - p.confirm() - } - } - - val system = ActorSystem("example") - val processor = system.actorOf(Props(classOf[ExampleProcessor]), "processor-1") - - processor ! Persistent("a") - processor ! Persistent("b") - - Thread.sleep(1000) - system.shutdown() -} diff --git a/akka-samples/akka-sample-persistence-scala/src/main/scala/sample/persistence/ProcessorChannelRemoteExample.scala b/akka-samples/akka-sample-persistence-scala/src/main/scala/sample/persistence/ProcessorChannelRemoteExample.scala deleted file mode 100644 index 8b9db2ba2a..0000000000 --- a/akka-samples/akka-sample-persistence-scala/src/main/scala/sample/persistence/ProcessorChannelRemoteExample.scala +++ /dev/null @@ -1,89 +0,0 @@ -package sample.persistence - -import scala.concurrent.duration._ - -import com.typesafe.config._ - -import akka.actor._ -import akka.persistence._ - -object ProcessorChannelRemoteExample { - val config = ConfigFactory.parseString( - """ - akka { - actor { - provider = "akka.remote.RemoteActorRefProvider" - } - remote { - enabled-transports = ["akka.remote.netty.tcp"] - netty.tcp.hostname = "127.0.0.1" - } - persistence { - journal.leveldb.dir = "target/example/journal" - snapshot-store.local.dir = "target/example/snapshots" - } - loglevel = INFO - log-dead-letters = 0 - log-dead-letters-during-shutdown = off - - } - """) -} - -object SenderApp /*extends App*/ { // no app until https://github.com/typesafehub/activator/issues/287 is fixed - import ProcessorChannelRemoteExample._ - - class ExampleProcessor(destination: ActorPath) extends Processor { - val listener = context.actorOf(Props[ExampleListener]) - val channel = context.actorOf(Channel.props(ChannelSettings( - redeliverMax = 15, - redeliverInterval = 3.seconds, - redeliverFailureListener = Some(listener))), "channel") - - def receive = { - case p @ Persistent(payload, snr) => - println(s"[processor] received payload: ${payload} (snr = ${snr}, replayed = ${recoveryRunning})") - channel ! Deliver(p.withPayload(s"processed ${payload}"), destination) - case "restart" => - throw new Exception("restart requested") - case reply: String => - println(s"[processor] received reply: ${reply}") - } - } - - class ExampleListener extends Actor { - def receive = { - case RedeliverFailure(messages) => - println(s"unable to deliver ${messages.length} messages, restarting processor to resend messages ...") - context.parent ! "restart" - } - } - - val receiverPath = ActorPath.fromString("akka.tcp://receiver@127.0.0.1:44317/user/receiver") - val senderConfig = ConfigFactory.parseString("akka.remote.netty.tcp.port = 44316") - - val system = ActorSystem("sender", config.withFallback(senderConfig)) - val sender = system.actorOf(Props(classOf[ExampleProcessor], receiverPath)) - - import system.dispatcher - - system.scheduler.schedule(Duration.Zero, 3.seconds, sender, Persistent("scheduled")) -} - -object ReceiverApp /*extends App*/ { // no app until https://github.com/typesafehub/activator/issues/287 is fixed - import ProcessorChannelRemoteExample._ - - class ExampleDestination extends Actor { - def receive = { - case p @ ConfirmablePersistent(payload, snr, redel) => - println(s"[destination] received payload: ${payload} (snr = ${snr}, redel = ${redel})") - sender() ! s"re: ${payload} (snr = ${snr})" - p.confirm() - } - } - - val receiverConfig = ConfigFactory.parseString("akka.remote.netty.tcp.port = 44317") - val system = ActorSystem("receiver", config.withFallback(receiverConfig)) - - system.actorOf(Props[ExampleDestination], "receiver") -} \ No newline at end of file diff --git a/akka-samples/akka-sample-persistence-scala/src/main/scala/sample/persistence/ProcessorFailureExample.scala b/akka-samples/akka-sample-persistence-scala/src/main/scala/sample/persistence/ProcessorFailureExample.scala deleted file mode 100644 index 32ca7be800..0000000000 --- a/akka-samples/akka-sample-persistence-scala/src/main/scala/sample/persistence/ProcessorFailureExample.scala +++ /dev/null @@ -1,56 +0,0 @@ -package sample.persistence - -import akka.actor._ -import akka.persistence._ - -object ProcessorFailureExample extends App { - class ExampleProcessor extends Processor { - var received: List[String] = Nil // state - - def receive = { - case "print" => println(s"received ${received.reverse}") - case "boom" => throw new Exception("boom") - case Persistent("boom", _) => throw new Exception("boom") - case Persistent(payload: String, _) => received = payload :: received - } - - override def preRestart(reason: Throwable, message: Option[Any]) { - message match { - case Some(p: Persistent) if !recoveryRunning => deleteMessage(p.sequenceNr) // mark failing message as deleted - case _ => // ignore - } - super.preRestart(reason, message) - } - } - - val system = ActorSystem("example") - val processor = system.actorOf(Props(classOf[ExampleProcessor]), "processor-2") - - processor ! Persistent("a") - processor ! "print" - processor ! "boom" // restart and recovery - processor ! "print" - processor ! Persistent("b") - processor ! "print" - processor ! Persistent("boom") // restart, recovery and deletion of message from journal - processor ! "print" - processor ! Persistent("c") - processor ! "print" - - // Will print in a first run (i.e. with empty journal): - - // received List(a) - // received List(a, b) - // received List(a, b, c) - - // Will print in a second run: - - // received List(a, b, c, a) - // received List(a, b, c, a, b) - // received List(a, b, c, a, b, c) - - // etc ... - - Thread.sleep(1000) - system.shutdown() -} diff --git a/akka-samples/akka-sample-persistence-scala/src/main/scala/sample/persistence/SnapshotExample.scala b/akka-samples/akka-sample-persistence-scala/src/main/scala/sample/persistence/SnapshotExample.scala index 0cdfa22419..48605e1558 100644 --- a/akka-samples/akka-sample-persistence-scala/src/main/scala/sample/persistence/SnapshotExample.scala +++ b/akka-samples/akka-sample-persistence-scala/src/main/scala/sample/persistence/SnapshotExample.scala @@ -5,34 +5,40 @@ import akka.persistence._ object SnapshotExample extends App { final case class ExampleState(received: List[String] = Nil) { - def update(s: String) = copy(s :: received) + def updated(s: String): ExampleState = copy(s :: received) override def toString = received.reverse.toString } - class ExampleProcessor extends Processor { + class ExamplePersistentActor extends PersistentActor { var state = ExampleState() - def receive = { - case Persistent(s, snr) => state = state.update(s"${s}-${snr}") + def receiveCommand: Actor.Receive = { + case "print" => println("current state = " + state) + case "snap" => saveSnapshot(state) case SaveSnapshotSuccess(metadata) => // ... case SaveSnapshotFailure(metadata, reason) => // ... + case s: String => + persist(s) { evt => state = state.updated(evt) } + } + + def receiveRecover: Actor.Receive = { case SnapshotOffer(_, s: ExampleState) => println("offered state = " + s) state = s - case "print" => println("current state = " + state) - case "snap" => saveSnapshot(state) + case evt: String => + state = state.updated(evt) } } val system = ActorSystem("example") - val processor = system.actorOf(Props(classOf[ExampleProcessor]), "processor-3-scala") + val persistentActor = system.actorOf(Props(classOf[ExamplePersistentActor]), "persistentActor-3-scala") - processor ! Persistent("a") - processor ! Persistent("b") - processor ! "snap" - processor ! Persistent("c") - processor ! Persistent("d") - processor ! "print" + persistentActor ! "a" + persistentActor ! "b" + persistentActor ! "snap" + persistentActor ! "c" + persistentActor ! "d" + persistentActor ! "print" Thread.sleep(1000) system.shutdown() diff --git a/akka-samples/akka-sample-persistence-scala/src/main/scala/sample/persistence/StreamExample.scala b/akka-samples/akka-sample-persistence-scala/src/main/scala/sample/persistence/StreamExample.scala index e495a9ce3b..651b1a975e 100644 --- a/akka-samples/akka-sample-persistence-scala/src/main/scala/sample/persistence/StreamExample.scala +++ b/akka-samples/akka-sample-persistence-scala/src/main/scala/sample/persistence/StreamExample.scala @@ -52,4 +52,4 @@ object StreamExample extends App { } } -*/ \ No newline at end of file +*/ diff --git a/akka-samples/akka-sample-persistence-scala/src/main/scala/sample/persistence/ViewExample.scala b/akka-samples/akka-sample-persistence-scala/src/main/scala/sample/persistence/ViewExample.scala index edaaec1ac5..53059cecfb 100644 --- a/akka-samples/akka-sample-persistence-scala/src/main/scala/sample/persistence/ViewExample.scala +++ b/akka-samples/akka-sample-persistence-scala/src/main/scala/sample/persistence/ViewExample.scala @@ -6,23 +6,30 @@ import akka.actor._ import akka.persistence._ object ViewExample extends App { - class ExampleProcessor extends Processor { - override def processorId = "processor-5" + class ExamplePersistentActor extends PersistentActor { + override def persistenceId = "persistentActor-5" - def receive = { - case Persistent(payload, sequenceNr) => - println(s"processor received ${payload} (sequence nr = ${sequenceNr})") + var count = 1 + + def receiveCommand: Actor.Receive = { + case payload: String => + println(s"persistentActor received ${payload} (nr = ${count})") + persist(payload + count) { evt => + count += 1 + } + } + + def receiveRecover: Actor.Receive = { + case _: String => count += 1 } } class ExampleView extends View { private var numReplicated = 0 - override def persistenceId: String = "processor-5" - override def viewId = "view-5" + override def persistenceId: String = "persistentActor-5" - private val destination = context.actorOf(Props[ExampleDestination]) - private val channel = context.actorOf(Channel.props("channel")) + override def viewId = "view-5" def receive = { case "snap" => @@ -30,29 +37,20 @@ object ViewExample extends App { case SnapshotOffer(metadata, snapshot: Int) => numReplicated = snapshot println(s"view received snapshot offer ${snapshot} (metadata = ${metadata})") - case Persistent(payload, sequenceNr) => + case Persistent(payload, _) => numReplicated += 1 - println(s"view received ${payload} (sequence nr = ${sequenceNr}, num replicated = ${numReplicated})") - channel ! Deliver(Persistent(s"replicated-${payload}"), destination.path) + println(s"view received ${payload} (num replicated = ${numReplicated})") } } - class ExampleDestination extends Actor { - def receive = { - case cp @ ConfirmablePersistent(payload, sequenceNr, _) => - println(s"destination received ${payload} (sequence nr = ${sequenceNr})") - cp.confirm() - } - } - val system = ActorSystem("example") - val processor = system.actorOf(Props(classOf[ExampleProcessor])) + val persistentActor = system.actorOf(Props(classOf[ExamplePersistentActor])) val view = system.actorOf(Props(classOf[ExampleView])) import system.dispatcher - system.scheduler.schedule(Duration.Zero, 2.seconds, processor, Persistent("scheduled")) + system.scheduler.schedule(Duration.Zero, 2.seconds, persistentActor, "scheduled") system.scheduler.schedule(Duration.Zero, 5.seconds, view, "snap") } diff --git a/akka-samples/akka-sample-persistence-scala/tutorial/index.html b/akka-samples/akka-sample-persistence-scala/tutorial/index.html index c9a2b56e25..73c992930e 100644 --- a/akka-samples/akka-sample-persistence-scala/tutorial/index.html +++ b/akka-samples/akka-sample-persistence-scala/tutorial/index.html @@ -11,12 +11,10 @@ This tutorial contains examples that illustrate a subset of Akka Persistence features.

    -
  • Processors and channels
  • -
  • Processsor snapshots
  • -
  • Eventsourced processors
  • -
  • Processor failure handling
  • -
  • Processor views
  • -
  • Processor conversation recovery
  • +
  • persistent actor
  • +
  • persistent actor snapshots
  • +
  • persistent actor recovery
  • +
  • persistent actor views

@@ -26,47 +24,27 @@ Custom storage locations for the journal and snapshots can be defined in

-

Processors and channels

+

Persistent actor

-ProcessorChannelExample.scala -defines an ExampleProcessor and an ExampleDestination. The processor sends messages to a -destination via a channel. The destination confirms the delivery of these messages so that replayed messages aren't -redundantly delivered to the destination. Repeated runs of the application demonstrates that the processor receives -both replayed and new messages whereas the channel only receives new messages, sent by the application. The processor -also receives replies from the destination, demonstrating that a channel preserves sender references. +PersistentActorExample.scala +is described in detail in the Event sourcing +section of the user documentation. With every application run, the ExamplePersistentActor is recovered from +events stored in previous application runs, processes new commands, stores new events and snapshots and prints the +current persistent actor state to stdout.

To run this example, go to the Run tab, and run the application main class -sample.persistence.ProcessorChannelExample several times. +sample.persistence.PersistentActorExample several times.

- - -
-

Processor snapshots

+

Persistent actor snapshots

SnapshotExample.scala -demonstrates how processors can take snapshots of application state and recover from previously stored snapshots. -Snapshots are offered to processors at the beginning of recovery, before any messages (younger than the snapshot) +demonstrates how persistent actors can take snapshots of application state and recover from previously stored snapshots. +Snapshots are offered to persistent actors at the beginning of recovery, before any messages (younger than the snapshot) are replayed.

@@ -74,23 +52,20 @@ are replayed. To run this example, go to the Run tab, and run the application main class sample.persistence.SnapshotExample several times. With every run, the state offered by the most recent snapshot is printed to stdout, followed by the updated state after sending new persistent -messages to the processor. +messages to the persistent actor.

-

Eventsourced processors

+

Persistent actor recovery

-EventsourcedExample.scala -is described in detail in the Event sourcing -section of the user documentation. With every application run, the ExampleProcessor is recovered from -events stored in previous application runs, processes new commands, stores new events and snapshots and prints the -current processor state to stdout. +PersistentActorFailureExample.scala +shows how a persistent actor can throw an exception, restart and restore the state by replaying the events.

To run this example, go to the Run tab, and run the application main class -sample.persistence.PersistentActorExample several times. +sample.persistence.PersistentActorFailureExample several times.

@@ -121,35 +96,20 @@ command.

Processor views

+

Persistent actor views

ViewExample.scala demonstrates -how a view (ExampleView) is updated with the persistent message stream of a processor -(ExampleProcessor). Messages sent to the processor are read from stdin. Views also support -snapshotting and can be used in combination with channels in the same way as processors. +how a view (ExampleView) is updated with the persistent message stream of a persistent actor +(ExamplePersistentActor). Messages sent to the persistent actor are scheduled periodically. Views also support +snapshotting to reduce recovery time.

To run this example, go to the Run tab, and run the application main class sample.persistence.ViewExample.

- -

-Views can also receive events that have been persisted by event sourced processors (not shown). -

-
-

Processor conversation recovery

-

-ConversationRecoveryExample.scala -defines two processors that send messages to each other via channels. The reliable delivery properties of channels, -in combination with processors, allow these processors to automatically resume their conversation after a JVM crash. -

-

-To run this example, go to the Run tab, and run the application main class -sample.persistence.ConversationRecoveryExample several times. -

-