=doc #18128 explain PoisonPill interaction with PA
This commit is contained in:
parent
7679b7e63f
commit
8c4cc9a93f
14 changed files with 341 additions and 22 deletions
|
|
@ -542,4 +542,74 @@ public class LambdaPersistenceDocTest {
|
|||
//#view-update
|
||||
}
|
||||
};
|
||||
|
||||
static Object o14 = new Object() {
|
||||
//#safe-shutdown
|
||||
final class Shutdown {
|
||||
}
|
||||
|
||||
class MyPersistentActor extends AbstractPersistentActor {
|
||||
@Override
|
||||
public String persistenceId() {
|
||||
return "some-persistence-id";
|
||||
}
|
||||
|
||||
@Override
|
||||
public PartialFunction<Object, BoxedUnit> receiveCommand() {
|
||||
return ReceiveBuilder
|
||||
.match(Shutdown.class, shutdown -> {
|
||||
context().stop(self());
|
||||
})
|
||||
.match(String.class, msg -> {
|
||||
System.out.println(msg);
|
||||
persist("handle-" + msg, e -> System.out.println(e));
|
||||
})
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PartialFunction<Object, BoxedUnit> receiveRecover() {
|
||||
return ReceiveBuilder.matchAny(any -> {}).build();
|
||||
}
|
||||
|
||||
}
|
||||
//#safe-shutdown
|
||||
|
||||
|
||||
public void usage() {
|
||||
final ActorSystem system = ActorSystem.create("example");
|
||||
final ActorRef persistentActor = system.actorOf(Props.create(MyPersistentActor.class));
|
||||
//#safe-shutdown-example-bad
|
||||
// UN-SAFE, due to PersistentActor's command stashing:
|
||||
persistentActor.tell("a", ActorRef.noSender());
|
||||
persistentActor.tell("b", ActorRef.noSender());
|
||||
persistentActor.tell(PoisonPill.getInstance(), ActorRef.noSender());
|
||||
// order of received messages:
|
||||
// a
|
||||
// # b arrives at mailbox, stashing; internal-stash = [b]
|
||||
// # PoisonPill arrives at mailbox, stashing; internal-stash = [b, Shutdown]
|
||||
// PoisonPill is an AutoReceivedMessage, is handled automatically
|
||||
// !! stop !!
|
||||
// Actor is stopped without handling `b` nor the `a` handler!
|
||||
//#safe-shutdown-example-bad
|
||||
|
||||
//#safe-shutdown-example-good
|
||||
// SAFE:
|
||||
persistentActor.tell("a", ActorRef.noSender());
|
||||
persistentActor.tell("b", ActorRef.noSender());
|
||||
persistentActor.tell(new Shutdown(), ActorRef.noSender());
|
||||
// order of received messages:
|
||||
// a
|
||||
// # b arrives at mailbox, stashing; internal-stash = [b]
|
||||
// # Shutdown arrives at mailbox, stashing; internal-stash = [b, Shutdown]
|
||||
// handle-a
|
||||
// # unstashing; internal-stash = [Shutdown]
|
||||
// b
|
||||
// handle-b
|
||||
// # unstashing; internal-stash = []
|
||||
// Shutdown
|
||||
// -- stop --
|
||||
//#safe-shutdown-example-good
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ import akka.japi.Function;
|
|||
import akka.japi.Procedure;
|
||||
import akka.persistence.*;
|
||||
|
||||
import scala.Option;
|
||||
import java.io.Serializable;
|
||||
|
||||
public class PersistenceDocTest {
|
||||
|
|
@ -504,7 +503,7 @@ public class PersistenceDocTest {
|
|||
}
|
||||
};
|
||||
|
||||
static Object o13 = new Object() {
|
||||
static Object o14 = new Object() {
|
||||
//#view
|
||||
class MyView extends UntypedPersistentView {
|
||||
@Override
|
||||
|
|
@ -534,4 +533,74 @@ public class PersistenceDocTest {
|
|||
//#view-update
|
||||
}
|
||||
};
|
||||
|
||||
static Object o13 = new Object() {
|
||||
//#safe-shutdown
|
||||
final class Shutdown {}
|
||||
|
||||
class MyPersistentActor extends UntypedPersistentActor {
|
||||
@Override
|
||||
public String persistenceId() {
|
||||
return "some-persistence-id";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceiveCommand(Object msg) throws Exception {
|
||||
if (msg instanceof Shutdown) {
|
||||
context().stop(self());
|
||||
} else if (msg instanceof String) {
|
||||
System.out.println(msg);
|
||||
persist("handle-" + msg, new Procedure<String>() {
|
||||
@Override
|
||||
public void apply(String param) throws Exception {
|
||||
System.out.println(param);
|
||||
}
|
||||
});
|
||||
} else unhandled(msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceiveRecover(Object msg) throws Exception {
|
||||
// handle recovery...
|
||||
}
|
||||
}
|
||||
//#safe-shutdown
|
||||
|
||||
|
||||
public void usage() {
|
||||
final ActorSystem system = ActorSystem.create("example");
|
||||
final ActorRef persistentActor = system.actorOf(Props.create(MyPersistentActor.class));
|
||||
//#safe-shutdown-example-bad
|
||||
// UN-SAFE, due to PersistentActor's command stashing:
|
||||
persistentActor.tell("a", ActorRef.noSender());
|
||||
persistentActor.tell("b", ActorRef.noSender());
|
||||
persistentActor.tell(PoisonPill.getInstance(), ActorRef.noSender());
|
||||
// order of received messages:
|
||||
// a
|
||||
// # b arrives at mailbox, stashing; internal-stash = [b]
|
||||
// # PoisonPill arrives at mailbox, stashing; internal-stash = [b, Shutdown]
|
||||
// PoisonPill is an AutoReceivedMessage, is handled automatically
|
||||
// !! stop !!
|
||||
// Actor is stopped without handling `b` nor the `a` handler!
|
||||
//#safe-shutdown-example-bad
|
||||
|
||||
//#safe-shutdown-example-good
|
||||
// SAFE:
|
||||
persistentActor.tell("a", ActorRef.noSender());
|
||||
persistentActor.tell("b", ActorRef.noSender());
|
||||
persistentActor.tell(new Shutdown(), ActorRef.noSender());
|
||||
// order of received messages:
|
||||
// a
|
||||
// # b arrives at mailbox, stashing; internal-stash = [b]
|
||||
// # Shutdown arrives at mailbox, stashing; internal-stash = [b, Shutdown]
|
||||
// handle-a
|
||||
// # unstashing; internal-stash = [Shutdown]
|
||||
// b
|
||||
// handle-b
|
||||
// # unstashing; internal-stash = []
|
||||
// Shutdown
|
||||
// -- stop --
|
||||
//#safe-shutdown-example-good
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -537,6 +537,8 @@ different one. Outside of an actor and if no reply is needed the second
|
|||
argument can be ``null``; if a reply is needed outside of an actor you can use
|
||||
the ask-pattern described next..
|
||||
|
||||
.. _actors-ask-lambda:
|
||||
|
||||
Ask: Send-And-Receive-Future
|
||||
----------------------------
|
||||
|
||||
|
|
|
|||
|
|
@ -376,10 +376,36 @@ restarts of the persistent actor.
|
|||
|
||||
.. _Thundering herd problem: https://en.wikipedia.org/wiki/Thundering_herd_problem
|
||||
|
||||
.. _safe-shutdown-lambda:
|
||||
|
||||
Safely shutting down persistent actors
|
||||
--------------------------------------
|
||||
|
||||
Special care should be given when when shutting down persistent actors from the outside.
|
||||
With normal Actors it is often acceptable to use the special :ref:`PoisonPill <poison-pill-java>` message
|
||||
to signal to an Actor that it should stop itself once it receives this message – in fact this message is handled
|
||||
automatically by Akka, leaving the target actor no way to refuse stopping itself when given a poison pill.
|
||||
|
||||
This can be dangerous when used with :class:`PersistentActor` due to the fact that incoming commands are *stashed* while
|
||||
the persistent actor is awaiting confirmation from the Journal that events have been written when ``persist()`` was used.
|
||||
Since the incoming commands will be drained from the Actor's mailbox and put into it's internal stash while awaiting the
|
||||
confirmation (thus, before calling the persist handlers) the Actor **may receive and (auto)handle the PoisonPill
|
||||
before it processes the other messages which have been put into its stash**, causing a pre-mature shutdown of the Actor.
|
||||
|
||||
.. warning::
|
||||
Consider using explicit shut-down messages instead of :class:`PoisonPill` when working with persistent actors.
|
||||
|
||||
The example below highlights how messages arrive in the Actor's mailbox and how they interact with it's internal stashing
|
||||
mechanism when ``persist()`` is used, notice the early stop behaviour that occurs when ``PoisonPill`` is used:
|
||||
|
||||
.. includecode:: code/docs/persistence/LambdaPersistenceDocTest.java#safe-shutdown
|
||||
.. includecode:: code/docs/persistence/LambdaPersistenceDocTest.java#safe-shutdown-example-bad
|
||||
.. includecode:: code/docs/persistence/LambdaPersistenceDocTest.java#safe-shutdown-example-good
|
||||
|
||||
.. _persistent-views-java-lambda:
|
||||
|
||||
Views
|
||||
=====
|
||||
Persistent Views
|
||||
================
|
||||
|
||||
.. warning::
|
||||
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ If your usage does not require a live stream, you can disable refreshing by usin
|
|||
|
||||
.. includecode:: code/docs/persistence/PersistenceQueryDocTest.java#all-persistence-ids-snap
|
||||
|
||||
``EventsByPersistenceId`` is a query equivalent to replaying a :ref:`PersistentActor <event-sourcing>`,
|
||||
``EventsByPersistenceId`` is a query equivalent to replaying a :ref:`PersistentActor <event-sourcing-scala>`,
|
||||
however, since it is a stream it is possible to keep it alive and watch for additional incoming events persisted by the
|
||||
persistent actor identified by the given ``persistenceId``. Most journals will have to revert to polling in order to achieve
|
||||
this, which can be configured using the ``RefreshInterval`` query hint:
|
||||
|
|
@ -135,7 +135,7 @@ specialised query object, as demonstrated in the sample below:
|
|||
|
||||
Performance and denormalization
|
||||
===============================
|
||||
When building systems using :ref:`event-sourcing` and CQRS (`Command & Query Responsibility Segragation`_) techniques
|
||||
When building systems using :ref:`event-sourcing-scala` and CQRS (`Command & Query Responsibility Segragation`_) techniques
|
||||
it is tremendously important to realise that the write-side has completely different needs from the read-side,
|
||||
and separating those concerns into datastores that are optimised for either side makes it possible to offer the best
|
||||
expirience for the write and read sides independently.
|
||||
|
|
|
|||
|
|
@ -379,6 +379,32 @@ restarts of the persistent actor.
|
|||
|
||||
.. _Thundering herd problem: https://en.wikipedia.org/wiki/Thundering_herd_problem
|
||||
|
||||
.. _safe-shutdown-java:
|
||||
|
||||
Safely shutting down persistent actors
|
||||
--------------------------------------
|
||||
|
||||
Special care should be given when when shutting down persistent actors from the outside.
|
||||
With normal Actors it is often acceptable to use the special :ref:`PoisonPill <poison-pill-java>` message
|
||||
to signal to an Actor that it should stop itself once it receives this message – in fact this message is handled
|
||||
automatically by Akka, leaving the target actor no way to refuse stopping itself when given a poison pill.
|
||||
|
||||
This can be dangerous when used with :class:`PersistentActor` due to the fact that incoming commands are *stashed* while
|
||||
the persistent actor is awaiting confirmation from the Journal that events have been written when ``persist()`` was used.
|
||||
Since the incoming commands will be drained from the Actor's mailbox and put into it's internal stash while awaiting the
|
||||
confirmation (thus, before calling the persist handlers) the Actor **may receive and (auto)handle the PoisonPill
|
||||
before it processes the other messages which have been put into its stash**, causing a pre-mature shutdown of the Actor.
|
||||
|
||||
.. warning::
|
||||
Consider using explicit shut-down messages instead of :class:`PoisonPill` when working with persistent actors.
|
||||
|
||||
The example below highlights how messages arrive in the Actor's mailbox and how they interact with it's internal stashing
|
||||
mechanism when ``persist()`` is used, notice the early stop behaviour that occurs when ``PoisonPill`` is used:
|
||||
|
||||
.. includecode:: code/docs/persistence/PersistenceDocTest.java#safe-shutdown
|
||||
.. includecode:: code/docs/persistence/PersistenceDocTest.java#safe-shutdown-example-bad
|
||||
.. includecode:: code/docs/persistence/PersistenceDocTest.java#safe-shutdown-example-good
|
||||
|
||||
.. _persistent-views-java:
|
||||
|
||||
Persistent Views
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue