+act #3911 Adding Java Lambda compatibility for Supervisor Strategy
Conflicts: akka-actor/src/main/scala/akka/util/Timeout.scala
This commit is contained in:
parent
95d27e3f82
commit
ba7247b240
18 changed files with 1141 additions and 27 deletions
|
|
@ -9,6 +9,7 @@ Java Documentation
|
|||
intro/index-java
|
||||
general/index
|
||||
java/index-actors
|
||||
java/lambda-index-actors
|
||||
java/index-futures
|
||||
java/index-network
|
||||
java/index-utilities
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import akka.actor.OneForOneStrategy;
|
|||
import akka.actor.Props;
|
||||
import akka.actor.Terminated;
|
||||
import akka.actor.UntypedActor;
|
||||
import scala.collection.immutable.Seq;
|
||||
import scala.concurrent.Await;
|
||||
import static akka.pattern.Patterns.ask;
|
||||
import scala.concurrent.duration.Duration;
|
||||
|
|
@ -31,7 +32,6 @@ import static java.util.concurrent.TimeUnit.SECONDS;
|
|||
import static akka.japi.Util.immutableSeq;
|
||||
import akka.japi.Function;
|
||||
import scala.Option;
|
||||
import scala.collection.immutable.Seq;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.BeforeClass;
|
||||
|
|
@ -164,12 +164,13 @@ public class FaultHandlingTest {
|
|||
public void mustEmploySupervisorStrategy() throws Exception {
|
||||
// code here
|
||||
//#testkit
|
||||
EventFilter ex1 = (EventFilter) new ErrorFilter(ArithmeticException.class);
|
||||
EventFilter ex2 = (EventFilter) new ErrorFilter(NullPointerException.class);
|
||||
EventFilter ex3 = (EventFilter) new ErrorFilter(IllegalArgumentException.class);
|
||||
EventFilter ex4 = (EventFilter) new ErrorFilter(Exception.class);
|
||||
Seq<EventFilter> ignoreExceptions = seq(ex1, ex2, ex3, ex4);
|
||||
system.eventStream().publish(new TestEvent.Mute(ignoreExceptions));
|
||||
EventFilter ex1 = new ErrorFilter(ArithmeticException.class);
|
||||
EventFilter ex2 = new ErrorFilter(NullPointerException.class);
|
||||
EventFilter ex3 = new ErrorFilter(IllegalArgumentException.class);
|
||||
EventFilter ex4 = new ErrorFilter(Exception.class);
|
||||
EventFilter[] ignoreExceptions = { ex1, ex2, ex3, ex4 };
|
||||
Seq<EventFilter> seq = immutableSeq(ignoreExceptions);
|
||||
system.eventStream().publish(new TestEvent.Mute(seq));
|
||||
|
||||
//#create
|
||||
Props superprops = Props.create(Supervisor.class);
|
||||
|
|
@ -219,11 +220,5 @@ public class FaultHandlingTest {
|
|||
//#testkit
|
||||
}
|
||||
|
||||
//#testkit
|
||||
@SuppressWarnings("unchecked")
|
||||
public <A> Seq<A> seq(A... args) {
|
||||
return immutableSeq(args);
|
||||
}
|
||||
//#testkit
|
||||
}
|
||||
//#testkit
|
||||
|
|
|
|||
|
|
@ -24,13 +24,6 @@ sample as it is easy to follow the log output to understand what is happening in
|
|||
|
||||
fault-tolerance-sample
|
||||
|
||||
.. note::
|
||||
|
||||
If the strategy is declared inside the supervising actor (as opposed to
|
||||
as a static property or class) its decider has access to all internal state of
|
||||
the actor in a thread-safe fashion, including obtaining a reference to the
|
||||
currently failed child (available as the ``getSender()`` of the failure message).
|
||||
|
||||
Creating a Supervisor Strategy
|
||||
------------------------------
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,3 @@ Actors
|
|||
fsm
|
||||
persistence
|
||||
testing
|
||||
lambda-actors
|
||||
lambda-fsm
|
||||
lambda-persistence
|
||||
|
|
|
|||
53
akka-docs/rst/java/lambda-fault-tolerance-sample.rst
Normal file
53
akka-docs/rst/java/lambda-fault-tolerance-sample.rst
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
.. _lambda-fault-tolerance-sample-java:
|
||||
|
||||
Diagrams of the Fault Tolerance Sample
|
||||
----------------------------------------------
|
||||
|
||||
.. image:: ../images/faulttolerancesample-normal-flow.png
|
||||
|
||||
*The above diagram illustrates the normal message flow.*
|
||||
|
||||
**Normal flow:**
|
||||
|
||||
======= ==================================================================================
|
||||
Step Description
|
||||
======= ==================================================================================
|
||||
1 The progress ``Listener`` starts the work.
|
||||
2 The ``Worker`` schedules work by sending ``Do`` messages periodically to itself
|
||||
3, 4, 5 When receiving ``Do`` the ``Worker`` tells the ``CounterService``
|
||||
to increment the counter, three times. The ``Increment`` message is forwarded
|
||||
to the ``Counter``, which updates its counter variable and sends current value
|
||||
to the ``Storage``.
|
||||
6, 7 The ``Worker`` asks the ``CounterService`` of current value of the counter and pipes
|
||||
the result back to the ``Listener``.
|
||||
======= ==================================================================================
|
||||
|
||||
|
||||
.. image:: ../images/faulttolerancesample-failure-flow.png
|
||||
|
||||
*The above diagram illustrates what happens in case of storage failure.*
|
||||
|
||||
**Failure flow:**
|
||||
|
||||
=========== ==================================================================================
|
||||
Step Description
|
||||
=========== ==================================================================================
|
||||
1 The ``Storage`` throws ``StorageException``.
|
||||
2 The ``CounterService`` is supervisor of the ``Storage`` and restarts the
|
||||
``Storage`` when ``StorageException`` is thrown.
|
||||
3, 4, 5, 6 The ``Storage`` continues to fail and is restarted.
|
||||
7 After 3 failures and restarts within 5 seconds the ``Storage`` is stopped by its
|
||||
supervisor, i.e. the ``CounterService``.
|
||||
8 The ``CounterService`` is also watching the ``Storage`` for termination and
|
||||
receives the ``Terminated`` message when the ``Storage`` has been stopped ...
|
||||
9, 10, 11 and tells the ``Counter`` that there is no ``Storage``.
|
||||
12 The ``CounterService`` schedules a ``Reconnect`` message to itself.
|
||||
13, 14 When it receives the ``Reconnect`` message it creates a new ``Storage`` ...
|
||||
15, 16 and tells the ``Counter`` to use the new ``Storage``
|
||||
=========== ==================================================================================
|
||||
|
||||
Full Source Code of the Fault Tolerance Sample
|
||||
------------------------------------------------------
|
||||
|
||||
.. includecode:: ../../../akka-samples/akka-docs-java-lambda/src/test/java/docs/actor/japi/FaultHandlingDocSample.java#all
|
||||
|
||||
175
akka-docs/rst/java/lambda-fault-tolerance.rst
Normal file
175
akka-docs/rst/java/lambda-fault-tolerance.rst
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
.. _lambda-fault-tolerance-java:
|
||||
|
||||
Fault Tolerance (Java with Lambda Support)
|
||||
===========================================
|
||||
|
||||
As explained in :ref:`actor-systems` each actor is the supervisor of its
|
||||
children, and as such each actor defines fault handling supervisor strategy.
|
||||
This strategy cannot be changed afterwards as it is an integral part of the
|
||||
actor system’s structure.
|
||||
|
||||
Fault Handling in Practice
|
||||
--------------------------
|
||||
|
||||
First, let us look at a sample that illustrates one way to handle data store errors,
|
||||
which is a typical source of failure in real world applications. Of course it depends
|
||||
on the actual application what is possible to do when the data store is unavailable,
|
||||
but in this sample we use a best effort re-connect approach.
|
||||
|
||||
Read the following source code. The inlined comments explain the different pieces of
|
||||
the fault handling and why they are added. It is also highly recommended to run this
|
||||
sample as it is easy to follow the log output to understand what is happening in runtime.
|
||||
|
||||
.. toctree::
|
||||
|
||||
lambda-fault-tolerance-sample
|
||||
|
||||
Creating a Supervisor Strategy
|
||||
------------------------------
|
||||
|
||||
The following sections explain the fault handling mechanism and alternatives
|
||||
in more depth.
|
||||
|
||||
For the sake of demonstration let us consider the following strategy:
|
||||
|
||||
.. includecode:: ../../../akka-samples/akka-docs-java-lambda/src/test/java/docs/actor/FaultHandlingTest.java
|
||||
:include: strategy
|
||||
|
||||
I have chosen a few well-known exception types in order to demonstrate the
|
||||
application of the fault handling directives described in :ref:`supervision`.
|
||||
First off, it is a one-for-one strategy, meaning that each child is treated
|
||||
separately (an all-for-one strategy works very similarly, the only difference
|
||||
is that any decision is applied to all children of the supervisor, not only the
|
||||
failing one). There are limits set on the restart frequency, namely maximum 10
|
||||
restarts per minute. ``-1`` and ``Duration.Inf()`` means that the respective limit
|
||||
does not apply, leaving the possibility to specify an absolute upper limit on the
|
||||
restarts or to make the restarts work infinitely.
|
||||
The child actor is stopped if the limit is exceeded.
|
||||
|
||||
.. note::
|
||||
|
||||
If the strategy is declared inside the supervising actor (as opposed to
|
||||
a separate class) its decider has access to all internal state of
|
||||
the actor in a thread-safe fashion, including obtaining a reference to the
|
||||
currently failed child (available as the ``getSender`` of the failure message).
|
||||
|
||||
Default Supervisor Strategy
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
``Escalate`` is used if the defined strategy doesn't cover the exception that was thrown.
|
||||
|
||||
When the supervisor strategy is not defined for an actor the following
|
||||
exceptions are handled by default:
|
||||
|
||||
* ``ActorInitializationException`` will stop the failing child actor
|
||||
* ``ActorKilledException`` will stop the failing child actor
|
||||
* ``Exception`` will restart the failing child actor
|
||||
* Other types of ``Throwable`` will be escalated to parent actor
|
||||
|
||||
If the exception escalate all the way up to the root guardian it will handle it
|
||||
in the same way as the default strategy defined above.
|
||||
|
||||
Stopping Supervisor Strategy
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Closer to the Erlang way is the strategy to just stop children when they fail
|
||||
and then take corrective action in the supervisor when DeathWatch signals the
|
||||
loss of the child. This strategy is also provided pre-packaged as
|
||||
:obj:`SupervisorStrategy.stoppingStrategy` with an accompanying
|
||||
:class:`StoppingSupervisorStrategy` configurator to be used when you want the
|
||||
``"/user"`` guardian to apply it.
|
||||
|
||||
Logging of Actor Failures
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
By default the ``SupervisorStrategy`` logs failures unless they are escalated.
|
||||
Escalated failures are supposed to be handled, and potentially logged, at a level
|
||||
higher in the hierarchy.
|
||||
|
||||
You can mute the default logging of a ``SupervisorStrategy`` by setting
|
||||
``loggingEnabled`` to ``false`` when instantiating it. Customized logging
|
||||
can be done inside the ``Decider``. Note that the reference to the currently
|
||||
failed child is available as the ``getSender`` when the ``SupervisorStrategy`` is
|
||||
declared inside the supervising actor.
|
||||
|
||||
You may also customize the logging in your own ``SupervisorStrategy`` implementation
|
||||
by overriding the ``logFailure`` method.
|
||||
|
||||
Supervision of Top-Level Actors
|
||||
-------------------------------
|
||||
|
||||
Toplevel actors means those which are created using ``system.actorOf()``, and
|
||||
they are children of the :ref:`User Guardian <user-guardian>`. There are no
|
||||
special rules applied in this case, the guardian simply applies the configured
|
||||
strategy.
|
||||
|
||||
Test Application
|
||||
----------------
|
||||
|
||||
The following section shows the effects of the different directives in practice,
|
||||
wherefor a test setup is needed. First off, we need a suitable supervisor:
|
||||
|
||||
.. includecode:: ../../../akka-samples/akka-docs-java-lambda/src/test/java/docs/actor/FaultHandlingTest.java
|
||||
:include: supervisor
|
||||
|
||||
This supervisor will be used to create a child, with which we can experiment:
|
||||
|
||||
.. includecode:: ../../../akka-samples/akka-docs-java-lambda/src/test/java/docs/actor/FaultHandlingTest.java
|
||||
:include: child
|
||||
|
||||
The test is easier by using the utilities described in :ref:`akka-testkit`,
|
||||
where ``TestProbe`` provides an actor ref useful for receiving and inspecting replies.
|
||||
|
||||
.. includecode:: ../../../akka-samples/akka-docs-java-lambda/src/test/java/docs/actor/FaultHandlingTest.java
|
||||
:include: testkit
|
||||
|
||||
Let us create actors:
|
||||
|
||||
.. includecode:: ../../../akka-samples/akka-docs-java-lambda/src/test/java/docs/actor/FaultHandlingTest.java
|
||||
:include: create
|
||||
|
||||
The first test shall demonstrate the ``Resume`` directive, so we try it out by
|
||||
setting some non-initial state in the actor and have it fail:
|
||||
|
||||
.. includecode:: ../../../akka-samples/akka-docs-java-lambda/src/test/java/docs/actor/FaultHandlingTest.java
|
||||
:include: resume
|
||||
|
||||
As you can see the value 42 survives the fault handling directive. Now, if we
|
||||
change the failure to a more serious ``NullPointerException``, that will no
|
||||
longer be the case:
|
||||
|
||||
.. includecode:: ../../../akka-samples/akka-docs-java-lambda/src/test/java/docs/actor/FaultHandlingTest.java
|
||||
:include: restart
|
||||
|
||||
And finally in case of the fatal ``IllegalArgumentException`` the child will be
|
||||
terminated by the supervisor:
|
||||
|
||||
.. includecode:: ../../../akka-samples/akka-docs-java-lambda/src/test/java/docs/actor/FaultHandlingTest.java
|
||||
:include: stop
|
||||
|
||||
Up to now the supervisor was completely unaffected by the child’s failure,
|
||||
because the directives set did handle it. In case of an ``Exception``, this is not
|
||||
true anymore and the supervisor escalates the failure.
|
||||
|
||||
.. includecode:: ../../../akka-samples/akka-docs-java-lambda/src/test/java/docs/actor/FaultHandlingTest.java
|
||||
:include: escalate-kill
|
||||
|
||||
The supervisor itself is supervised by the top-level actor provided by the
|
||||
:class:`ActorSystem`, which has the default policy to restart in case of all
|
||||
``Exception`` cases (with the notable exceptions of
|
||||
``ActorInitializationException`` and ``ActorKilledException``). Since the
|
||||
default directive in case of a restart is to kill all children, we expected our poor
|
||||
child not to survive this failure.
|
||||
|
||||
In case this is not desired (which depends on the use case), we need to use a
|
||||
different supervisor which overrides this behavior.
|
||||
|
||||
.. includecode:: ../../../akka-samples/akka-docs-java-lambda/src/test/java/docs/actor/FaultHandlingTest.java
|
||||
:include: supervisor2
|
||||
|
||||
With this parent, the child survives the escalated restart, as demonstrated in
|
||||
the last test:
|
||||
|
||||
.. includecode:: ../../../akka-samples/akka-docs-java-lambda/src/test/java/docs/actor/FaultHandlingTest.java
|
||||
:include: escalate-restart
|
||||
|
||||
10
akka-docs/rst/java/lambda-index-actors.rst
Normal file
10
akka-docs/rst/java/lambda-index-actors.rst
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
Actors (Java with Lambda Support)
|
||||
=================================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
lambda-actors
|
||||
lambda-fault-tolerance
|
||||
lambda-fsm
|
||||
lambda-persistence
|
||||
Loading…
Add table
Add a link
Reference in a new issue