Merge pull request #17828 from ktoso/wip-nested-persist-ktoso

~per #15640 support nested persist calls
This commit is contained in:
Konrad Malawski 2015-06-26 12:53:16 +02:00
commit 83e931ea7e
10 changed files with 752 additions and 46 deletions

View file

@ -41,7 +41,7 @@ public class PersistenceDocTest {
private void recover() {
//#recover-explicit
persistentActor.tell(Recover.create(), getSelf());
persistentActor.tell(Recover.create(), self());
//#recover-explicit
}
}
@ -64,7 +64,7 @@ public class PersistenceDocTest {
//#recover-on-start-custom
@Override
public void preStart() {
getSelf().tell(Recover.create(457L), getSelf());
self().tell(Recover.create(457L), self());
}
//#recover-on-start-custom
}
@ -129,7 +129,7 @@ public class PersistenceDocTest {
abstract class MyPersistentActor1 extends UntypedPersistentActor {
//#recover-fully-disabled
@Override
public void preStart() { getSelf().tell(Recover.create(0L), getSelf()); }
public void preStart() { self().tell(Recover.create(0L), self()); }
//#recover-fully-disabled
}
};
@ -227,7 +227,7 @@ public class PersistenceDocTest {
if (message instanceof Msg) {
Msg msg = (Msg) message;
// ...
getSender().tell(new Confirm(msg.deliveryId), getSelf());
getSender().tell(new Confirm(msg.deliveryId), self());
} else {
unhandled(message);
}
@ -324,7 +324,7 @@ public class PersistenceDocTest {
@Override
public void onReceiveCommand(Object msg) {
sender().tell(msg, getSelf());
sender().tell(msg, self());
persistAsync(String.format("evt-%s-1", msg), new Procedure<String>(){
@Override
@ -376,7 +376,7 @@ public class PersistenceDocTest {
final Procedure<String> replyToSender = new Procedure<String>() {
@Override
public void apply(String event) throws Exception {
sender().tell(event, getSelf());
sender().tell(event, self());
}
};
@ -408,6 +408,140 @@ public class PersistenceDocTest {
};
static Object o11 = new Object() {
class MyPersistentActor extends UntypedPersistentActor {
@Override
public String persistenceId() {
return "my-stable-persistence-id";
}
@Override
public void onReceiveRecover(Object msg) {
// handle recovery here
}
//#nested-persist-persist
@Override
public void onReceiveCommand(Object msg) {
final Procedure<String> replyToSender = new Procedure<String>() {
@Override
public void apply(String event) throws Exception {
sender().tell(event, self());
}
};
final Procedure<String> outer1Callback = new Procedure<String>() {
@Override
public void apply(String event) throws Exception {
sender().tell(event, self());
persist(String.format("%s-inner-1", msg), replyToSender);
}
};
final Procedure<String> outer2Callback = new Procedure<String>() {
@Override
public void apply(String event) throws Exception {
sender().tell(event, self());
persist(String.format("%s-inner-2", msg), replyToSender);
}
};
persist(String.format("%s-outer-1", msg), outer1Callback);
persist(String.format("%s-outer-2", msg), outer2Callback);
}
//#nested-persist-persist
void usage(ActorRef persistentActor) {
//#nested-persist-persist-caller
persistentActor.tell("a", self());
persistentActor.tell("b", self());
// order of received messages:
// a
// a-outer-1
// a-outer-2
// a-inner-1
// a-inner-2
// and only then process "b"
// b
// b-outer-1
// b-outer-2
// b-inner-1
// b-inner-2
//#nested-persist-persist-caller
}
}
class MyPersistAsyncActor extends UntypedPersistentActor {
@Override
public String persistenceId() {
return "my-stable-persistence-id";
}
@Override
public void onReceiveRecover(Object msg) {
// handle recovery here
}
//#nested-persistAsync-persistAsync
@Override
public void onReceiveCommand(Object msg) {
final Procedure<String> replyToSender = new Procedure<String>() {
@Override
public void apply(String event) throws Exception {
sender().tell(event, self());
}
};
final Procedure<String> outer1Callback = new Procedure<String>() {
@Override
public void apply(String event) throws Exception {
sender().tell(event, self());
persistAsync(String.format("%s-inner-1", msg), replyToSender);
}
};
final Procedure<String> outer2Callback = new Procedure<String>() {
@Override
public void apply(String event) throws Exception {
sender().tell(event, self());
persistAsync(String.format("%s-inner-1", msg), replyToSender);
}
};
persistAsync(String.format("%s-outer-1", msg), outer1Callback);
persistAsync(String.format("%s-outer-2", msg), outer2Callback);
}
//#nested-persistAsync-persistAsync
void usage(ActorRef persistentActor) {
//#nested-persistAsync-persistAsync-caller
persistentActor.tell("a", ActorRef.noSender());
persistentActor.tell("b", ActorRef.noSender());
// order of received messages:
// a
// b
// a-outer-1
// a-outer-2
// b-outer-1
// b-outer-2
// a-inner-1
// a-inner-2
// b-inner-1
// b-inner-2
// which can be seen as the following causal relationship:
// a -> a-outer-1 -> a-outer-2 -> a-inner-1 -> a-inner-2
// b -> b-outer-1 -> b-outer-2 -> b-inner-1 -> b-inner-2
//#nested-persistAsync-persistAsync-caller
}
}
};
static Object o12 = new Object() {
//#view
class MyView extends UntypedPersistentView {
@Override

View file

@ -249,7 +249,7 @@ The ordering between events is still guaranteed ("evt-b-1" will be sent after "e
.. _defer-java-lambda:
Deferring actions until preceding 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
@ -270,6 +270,41 @@ of the command for which this ``defer`` handler was called.
The callback will not be invoked if the actor is restarted (or stopped) in between the call to
``defer`` and the journal has processed and confirmed all preceding writes.
.. _nested-persist-calls-lambda:
Nested persist calls
--------------------
It is possible to call ``persist`` and ``persistAsync`` inside their respective callback blocks and they will properly
retain both the thread safety (including the right value of ``sender()``) as well as stashing guarantees.
In general it is encouraged to create command handlers which do not need to resort to nested event persisting,
however there are situations where it may be useful. It is important to understand the ordering of callback execution in
those situations, as well as their implication on the stashing behaviour (that ``persist()`` enforces). In the following
example two persist calls are issued, and each of them issues another persist inside its callback:
.. includecode:: ../../../akka-samples/akka-sample-persistence-java-lambda/src/main/java/doc/LambdaPersistenceDocTest.java#nested-persist-persist
When sending two commands to this ``PersistentActor``, the persist handlers will be executed in the following order:
.. includecode:: ../../../akka-samples/akka-sample-persistence-java-lambda/src/main/java/doc/LambdaPersistenceDocTest.java#nested-persist-persist-caller
First the "outer layer" of persist calls is issued and their callbacks applied, after these have successfully completed
the inner callbacks will be invoked (once the events they are persisting have been confirmed to be persisted by the journal).
And only after all these handlers have been successfully invoked, the next command will delivered to the persistent Actor.
In other words, the stashing of incoming commands that is guaranteed by initially calling ``persist()`` on the outer layer
is extended until all nested ``persist`` callbacks have been handled.
It is also possible to nest ``persistAsync`` calls, using the same pattern:
.. includecode:: ../../../akka-samples/akka-sample-persistence-java-lambda/src/main/java/doc/LambdaPersistenceDocTest.java#nested-persistAsync-persistAsync
In this case no stashing is happening, yet the events are still persisted and callbacks executed in the expected order:
.. includecode:: ../../../akka-samples/akka-sample-persistence-java-lambda/src/main/java/doc/LambdaPersistenceDocTest.java#nested-persistAsync-persistAsync-caller
While it is possible to nest mixed ``persist`` and ``persistAsync`` with keeping their respective semantics
it is not a recommended practice as it may lead to overly complex nesting.
Failures
--------

View file

@ -273,6 +273,41 @@ of the command for which this ``defer`` handler was called.
The callback will not be invoked if the actor is restarted (or stopped) in between the call to
``defer`` and the journal has processed and confirmed all preceding writes.
.. _nested-persist-calls-java:
Nested persist calls
--------------------
It is possible to call ``persist`` and ``persistAsync`` inside their respective callback blocks and they will properly
retain both the thread safety (including the right value of ``sender()``) as well as stashing guarantees.
In general it is encouraged to create command handlers which do not need to resort to nested event persisting,
however there are situations where it may be useful. It is important to understand the ordering of callback execution in
those situations, as well as their implication on the stashing behaviour (that ``persist()`` enforces). In the following
example two persist calls are issued, and each of them issues another persist inside its callback:
.. includecode:: code/docs/persistence/PersistenceDocTest.java#nested-persist-persist
When sending two commands to this ``PersistentActor``, the persist handlers will be executed in the following order:
.. includecode:: code/docs/persistence/PersistenceDocTest.java#nested-persist-persist-caller
First the "outer layer" of persist calls is issued and their callbacks applied, after these have successfully completed
the inner callbacks will be invoked (once the events they are persisting have been confirmed to be persisted by the journal).
And only after all these handlers have been successfully invoked, the next command will delivered to the persistent Actor.
In other words, the stashing of incoming commands that is guaranteed by initially calling ``persist()`` on the outer layer
is extended until all nested ``persist`` callbacks have been handled.
It is also possible to nest ``persistAsync`` calls, using the same pattern:
.. includecode:: code/docs/persistence/PersistenceDocTest.java#nested-persistAsync-persistAsync
In this case no stashing is happening, yet the events are still persisted and callbacks executed in the expected order:
.. includecode:: code/docs/persistence/PersistenceDocTest.java#nested-persistAsync-persistAsync-caller
While it is possible to nest mixed ``persist`` and ``persistAsync`` with keeping their respective semantics
it is not a recommended practice as it may lead to overly complex nesting.
Failures
--------