!per persistAsync
Breaks binary compatibility because adding new methods to Eventsourced trait. Since akka-persistence is experimental this is ok, yet source-level compatibility has been perserved thankfuly :-) Deprecates: * Rename of EventsourcedProcessor -> PersistentActor * Processor -> suggest using PersistentActor * Migration guide for akka-persistence is separate, as wel'll deprecate in minor versions (its experimental) * Persistent as well as ConfirmablePersistent - since Processor, their main user will be removed soon. Other changes: * persistAsync works as expected when mixed with persist * A counter must be kept for pending stashing invocations * Uses only 1 shared list buffer for persit / persistAsync * Includes small benchmark * Docs also include info about not using Persistent() wrapper * uses java LinkedList, for best performance of append / head on persistInvocations; the get(0) is safe, because these msgs only come in response to persistInvocations * Renamed internal *MessagesSuccess/Failure messages because we kept small mistakes seeing the class "with s" and "without s" as the same * Updated everything that refered to EventsourcedProcessor to PersistentActor, including samples Refs #15227 Conflicts: akka-docs/rst/project/migration-guides.rst akka-persistence/src/main/scala/akka/persistence/JournalProtocol.scala akka-persistence/src/main/scala/akka/persistence/Persistent.scala akka-persistence/src/test/scala/akka/persistence/PersistentActorSpec.scala project/AkkaBuild.scala
This commit is contained in:
parent
5f3d6029b1
commit
d51b79c95a
32 changed files with 907 additions and 134 deletions
|
|
@ -4,13 +4,17 @@
|
|||
|
||||
package docs.persistence;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import scala.Option;
|
||||
import scala.concurrent.duration.Duration;
|
||||
import akka.actor.*;
|
||||
import akka.actor.ActorRef;
|
||||
import akka.actor.ActorSystem;
|
||||
import akka.actor.Props;
|
||||
import akka.actor.UntypedActor;
|
||||
import akka.japi.Procedure;
|
||||
import akka.persistence.*;
|
||||
import scala.Option;
|
||||
import scala.concurrent.duration.Duration;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
public class PersistenceDocTest {
|
||||
|
|
@ -353,11 +357,11 @@ public class PersistenceDocTest {
|
|||
|
||||
static Object o8 = new Object() {
|
||||
//#reliable-event-delivery
|
||||
class MyEventsourcedProcessor extends UntypedEventsourcedProcessor {
|
||||
class MyPersistentActor extends UntypedPersistentActor {
|
||||
private ActorRef destination;
|
||||
private ActorRef channel;
|
||||
|
||||
public MyEventsourcedProcessor(ActorRef destination) {
|
||||
public MyPersistentActor(ActorRef destination) {
|
||||
this.destination = destination;
|
||||
this.channel = getContext().actorOf(Channel.props(), "channel");
|
||||
}
|
||||
|
|
@ -390,6 +394,53 @@ public class PersistenceDocTest {
|
|||
};
|
||||
|
||||
static Object o9 = new Object() {
|
||||
//#persist-async
|
||||
class MyPersistentActor extends UntypedPersistentActor {
|
||||
|
||||
@Override
|
||||
public void onReceiveRecover(Object msg) {
|
||||
// handle recovery here
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceiveCommand(Object msg) {
|
||||
sender().tell(msg, getSelf());
|
||||
|
||||
persistAsync(String.format("evt-%s-1", msg), new Procedure<String>(){
|
||||
@Override
|
||||
public void apply(String event) throws Exception {
|
||||
sender().tell(event, self());
|
||||
}
|
||||
});
|
||||
persistAsync(String.format("evt-%s-2", msg), new Procedure<String>(){
|
||||
@Override
|
||||
public void apply(String event) throws Exception {
|
||||
sender().tell(event, self());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
//#persist-async
|
||||
|
||||
public void usage() {
|
||||
final ActorSystem system = ActorSystem.create("example");
|
||||
//#view-update
|
||||
final ActorRef processor = system.actorOf(Props.create(MyPersistentActor.class));
|
||||
processor.tell("a", null);
|
||||
processor.tell("b", null);
|
||||
|
||||
// possible order of received messages:
|
||||
// a
|
||||
// b
|
||||
// evt-a-1
|
||||
// evt-a-2
|
||||
// evt-b-1
|
||||
// evt-b-2
|
||||
//#view-update
|
||||
}
|
||||
};
|
||||
|
||||
static Object o10 = new Object() {
|
||||
//#view
|
||||
class MyView extends UntypedView {
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -442,13 +442,13 @@ also process commands that do not change application state, such as query comman
|
|||
|
||||
.. _Event Sourcing: http://martinfowler.com/eaaDev/EventSourcing.html
|
||||
|
||||
Akka persistence supports event sourcing with the ``AbstractEventsourcedProcessor`` abstract class (which implements
|
||||
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
|
||||
``AbstractEventsourcedProcessor`` is defined by implementing ``receiveRecover`` and ``receiveCommand``. This is
|
||||
``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/EventsourcedExample.java#eventsourced-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``.
|
||||
|
|
@ -473,7 +473,7 @@ calls in context of a single command.
|
|||
|
||||
The easiest way to run this example yourself is to download `Typesafe Activator <http://www.typesafe.com/platform/getstarted>`_
|
||||
and open the tutorial named `Akka Persistence Samples in Java with Lambdas <http://www.typesafe.com/activator/template/akka-sample-persistence-java-lambda>`_.
|
||||
It contains instructions on how to run the ``EventsourcedExample``.
|
||||
It contains instructions on how to run the ``PersistentActorExample``.
|
||||
|
||||
.. note::
|
||||
|
||||
|
|
@ -518,9 +518,9 @@ Applications that want to have more explicit control over batch writes and 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 ``AbstractEventsourcedProcessor`` to ensure atomic
|
||||
``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 ``AbstractEventsourcedProcessor``
|
||||
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
|
||||
|
|
|
|||
|
|
@ -48,9 +48,14 @@ Akka persistence is a separate jar file. Make sure that you have the following d
|
|||
Architecture
|
||||
============
|
||||
|
||||
* *Processor*: 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.
|
||||
* *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
|
||||
|
|
@ -79,6 +84,12 @@ Architecture
|
|||
Processors
|
||||
==========
|
||||
|
||||
.. 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 <https://github.com/akka/akka/issues/15230>`_.
|
||||
|
||||
A processor can be implemented by extending the abstract ``UntypedProcessor`` class and implementing the
|
||||
``onReceive`` method.
|
||||
|
||||
|
|
@ -451,6 +462,11 @@ use the ``deleteSnapshots`` method.
|
|||
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
|
||||
|
|
@ -468,13 +484,13 @@ also process commands that do not change application state, such as query comman
|
|||
|
||||
.. _Event Sourcing: http://martinfowler.com/eaaDev/EventSourcing.html
|
||||
|
||||
Akka persistence supports event sourcing with the abstract ``UntypedEventsourcedProcessor`` class (which implements
|
||||
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
|
||||
``UntypedEventsourcedProcessor`` is defined by implementing ``onReceiveRecover`` and ``onReceiveCommand``. This is
|
||||
``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/EventsourcedExample.java#eventsourced-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``.
|
||||
|
|
@ -499,7 +515,7 @@ calls in context of a single command.
|
|||
|
||||
The easiest way to run this example yourself is to download `Typesafe Activator <http://www.typesafe.com/platform/getstarted>`_
|
||||
and open the tutorial named `Akka Persistence Samples with Java <http://www.typesafe.com/activator/template/akka-sample-persistence-java>`_.
|
||||
It contains instructions on how to run the ``EventsourcedExample``.
|
||||
It contains instructions on how to run the ``PersistentActorExample``.
|
||||
|
||||
.. note::
|
||||
|
||||
|
|
@ -508,6 +524,29 @@ It contains instructions on how to run the ``EventsourcedExample``.
|
|||
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
|
||||
|
||||
.. note::
|
||||
In order to implement the pattern known as "*command sourcing*" simply ``persistAsync`` all incoming events right away,
|
||||
and handle them in the callback.
|
||||
|
||||
Reliable event delivery
|
||||
-----------------------
|
||||
|
|
@ -543,9 +582,9 @@ Applications that want to have more explicit control over batch writes and batch
|
|||
size is greater than ``max-message-batch-size``. Also, a ``PersistentBatch`` is written isolated from other batches.
|
||||
``Persistent`` messages contained in a ``PersistentBatch`` are received individually by a processor.
|
||||
|
||||
``PersistentBatch`` messages, for example, are used internally by an ``UntypedEventsourcedProcessor`` to ensure atomic
|
||||
``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 ``UntypedEventsourcedProcessor``
|
||||
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
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
.. _migration-eventsourced-2.3:
|
||||
|
||||
#######################################################
|
||||
Migration Guide Eventsourced to Akka Persistence 2.3.x
|
||||
#######################################################
|
||||
######################################################
|
||||
Migration Guide Eventsourced to Akka Persistence 2.3.x
|
||||
######################################################
|
||||
|
||||
General notes
|
||||
=============
|
||||
|
|
|
|||
|
|
@ -0,0 +1,67 @@
|
|||
.. _migration-guide-persistence-experimental-2.3.x-2.4.x:
|
||||
|
||||
#####################################################
|
||||
Migration Guide Akka Persistence (experimental) 2.3.3
|
||||
#####################################################
|
||||
|
||||
**Akka Persistence** is an **experimental module**, which means that neither Binary Compatibility nor API stability
|
||||
is provided for Persistence while under the *experimental* flag. The goal of this phase is to gather user feedback
|
||||
before we freeze the APIs in a major release.
|
||||
|
||||
Renamed EventsourcedProcessor to PersistentActor
|
||||
================================================
|
||||
``EventsourcedProcessor`` is now deprecated and replaced by ``PersistentActor`` which provides the same (and more) API.
|
||||
Migrating to ``2.4.x`` is as simple as changing all your classes to extending ``PersistentActor``.
|
||||
|
||||
Replace all classes like::
|
||||
|
||||
class DeprecatedProcessor extends EventsourcedProcessor { /*...*/ }
|
||||
|
||||
To extend ``PersistentActor``::
|
||||
|
||||
class NewPersistentProcessor extends PersistentActor { /*...*/ }
|
||||
|
||||
No other API changes are required for this migration.
|
||||
|
||||
Removed Processor in favour of extending PersistentActor with persistAsync
|
||||
==========================================================================
|
||||
|
||||
The ``Processor`` is now deprecated since ``2.3.4`` and will be removed in ``2.4.x``.
|
||||
It's semantics replicated in ``PersistentActor`` in the form of an additional ``persist`` method: ``persistAsync``.
|
||||
|
||||
In essence, the difference betwen ``persist`` and ``persistAsync`` is that the former will stash all incomming commands
|
||||
until all persist callbacks have been processed, whereas the latter does not stash any commands. The new ``persistAsync``
|
||||
should be used in cases of low consistency yet high responsiveness requirements, the Actor can keep processing incomming
|
||||
commands, even though not all previous events have been handled.
|
||||
|
||||
When these ``persist`` and ``persistAsync`` are used together in the same ``PersistentActor``, the ``persist``
|
||||
logic will win over the async version so that all guarantees concerning persist still hold. This will however lower
|
||||
the throughput
|
||||
|
||||
Now deprecated code using Processor::
|
||||
|
||||
class OldProcessor extends Processor {
|
||||
def receive = {
|
||||
case Persistent(cmd) => sender() ! cmd
|
||||
}
|
||||
}
|
||||
|
||||
Replacement code, with the same semantics, using PersistentActor::
|
||||
|
||||
class NewProcessor extends PersistentActor {
|
||||
def receiveCommand = {
|
||||
case cmd =>
|
||||
persistAsync(cmd) { e => sender() ! e }
|
||||
}
|
||||
|
||||
def receiveEvent = {
|
||||
case _ => // logic for handling replay
|
||||
}
|
||||
}
|
||||
|
||||
It is worth pointing out that using ``sender()`` inside the persistAsync callback block is **valid**, and does *not* suffer
|
||||
any of the problems Futures have when closing over the sender reference.
|
||||
|
||||
Using the``PersistentActor`` instead of ``Processor`` also shifts the responsibility of deciding if a message should be persisted
|
||||
to the receiver instead of the sender of the message. Previously, using ``Processor``, clients would have to wrap messages as ``Persistent(cmd)``
|
||||
manually, as well as have to be aware of the receiver being a ``Processor``, which didn't play well with transparency of the ActorRefs in general.
|
||||
|
|
@ -11,4 +11,5 @@ Migration Guides
|
|||
migration-guide-2.1.x-2.2.x
|
||||
migration-guide-2.2.x-2.3.x
|
||||
migration-guide-2.3.x-2.4.x
|
||||
migration-guide-persistence-experimental-2.3.x-2.4.x
|
||||
migration-guide-eventsourced-2.3.x
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ package docs.persistence
|
|||
import scala.concurrent.duration._
|
||||
import scala.language.postfixOps
|
||||
|
||||
import akka.actor.{ Actor, ActorSystem }
|
||||
import akka.actor.{ Props, Actor, ActorSystem }
|
||||
import akka.persistence._
|
||||
|
||||
trait PersistenceDocSpec {
|
||||
|
|
@ -314,7 +314,7 @@ trait PersistenceDocSpec {
|
|||
import akka.actor.ActorRef
|
||||
|
||||
//#reliable-event-delivery
|
||||
class MyEventsourcedProcessor(destination: ActorRef) extends EventsourcedProcessor {
|
||||
class MyPersistentActor(destination: ActorRef) extends PersistentActor {
|
||||
val channel = context.actorOf(Channel.props("channel"))
|
||||
|
||||
def handleEvent(event: String) = {
|
||||
|
|
@ -337,6 +337,42 @@ trait PersistenceDocSpec {
|
|||
}
|
||||
//#reliable-event-delivery
|
||||
}
|
||||
|
||||
new AnyRef {
|
||||
import akka.actor.ActorRef
|
||||
|
||||
val processor = system.actorOf(Props[MyPersistentActor]())
|
||||
|
||||
//#persist-async
|
||||
class MyPersistentActor extends PersistentActor {
|
||||
|
||||
def receiveRecover: Receive = {
|
||||
case _ => // handle recovery here
|
||||
}
|
||||
|
||||
def receiveCommand: Receive = {
|
||||
case c: String => {
|
||||
sender() ! c
|
||||
persistAsync(s"evt-$c-1") { e => sender() ! e }
|
||||
persistAsync(s"evt-$c-2") { e => sender() ! e }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// usage
|
||||
processor ! "a"
|
||||
processor ! "b"
|
||||
|
||||
// possible order of received messages:
|
||||
// a
|
||||
// b
|
||||
// evt-a-1
|
||||
// evt-a-2
|
||||
// evt-b-1
|
||||
// evt-b-2
|
||||
|
||||
//#persist-async
|
||||
}
|
||||
new AnyRef {
|
||||
import akka.actor.Props
|
||||
|
||||
|
|
|
|||
|
|
@ -36,9 +36,14 @@ Akka persistence is a separate jar file. Make sure that you have the following d
|
|||
Architecture
|
||||
============
|
||||
|
||||
* *Processor*: A processor is a persistent, stateful actor. Messages sent to a processor are written to a journal
|
||||
before its ``receive`` 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.
|
||||
* *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
|
||||
|
|
@ -67,6 +72,12 @@ Architecture
|
|||
Processors
|
||||
==========
|
||||
|
||||
.. 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 <https://github.com/akka/akka/issues/15230>`_.
|
||||
|
||||
A processor can be implemented by extending the ``Processor`` trait and implementing the ``receive`` method.
|
||||
|
||||
.. includecode:: code/docs/persistence/PersistenceDocSpec.scala#definition
|
||||
|
|
@ -463,6 +474,11 @@ use the ``deleteSnapshots`` method.
|
|||
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
|
||||
|
|
@ -480,12 +496,12 @@ also process commands that do not change application state, such as query comman
|
|||
|
||||
.. _Event Sourcing: http://martinfowler.com/eaaDev/EventSourcing.html
|
||||
|
||||
Akka persistence supports event sourcing with the ``EventsourcedProcessor`` trait (which implements event sourcing
|
||||
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 ``EventsourcedProcessor``
|
||||
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/EventsourcedExample.scala#eventsourced-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``.
|
||||
|
|
@ -510,7 +526,7 @@ calls in context of a single command.
|
|||
|
||||
The easiest way to run this example yourself is to download `Typesafe Activator <http://www.typesafe.com/platform/getstarted>`_
|
||||
and open the tutorial named `Akka Persistence Samples with Scala <http://www.typesafe.com/activator/template/akka-sample-persistence-scala>`_.
|
||||
It contains instructions on how to run the ``EventsourcedExample``.
|
||||
It contains instructions on how to run the ``PersistentActorExample``.
|
||||
|
||||
.. note::
|
||||
|
||||
|
|
@ -519,6 +535,34 @@ It contains instructions on how to run the ``EventsourcedExample``.
|
|||
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 this decision
|
||||
was made by the sender.
|
||||
|
||||
|
||||
.. 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.
|
||||
|
||||
Reliable event delivery
|
||||
-----------------------
|
||||
|
|
@ -556,9 +600,9 @@ Applications that want to have more explicit control over batch writes and 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 ``EventsourcedProcessor`` to ensure atomic
|
||||
``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 ``EventsourcedProcessor``
|
||||
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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue