Java API here. I am trying to understand how Akka actor supervision really works, specifically supervisor strategies. To me, the strategy feels like its just (essentially) a mapping of exceptions to directives.
I just read the official Akka Fault Tolerance documentation, but nowhere in that doc do they actually, explicitly state when/where/how supervision strategies are triggered.
So I ask: when are supervision strategies engaged? When the child actor throws a Throwable
from inside its onReceive
method? Or something different?
You are correct except it includes throwables, that is Error, as well as Exception, in a default decider rule and it operates any time an Actor throws a Throwable. According to http://doc.akka.io/docs/akka/2.0/java/fault-tolerance.html#default-supervisor-strategy, by default if there is no defined supervisor strategy or it does not cover an exception thrown by a (user created) Actor, the following rules are executed in order until one is triggered:
An ok review of supervision is at http://doc.akka.io/docs/akka/2.3.11/general/supervision.html if you have not already seen it.
A good discussion of it at the code level is in "Akka Concurrency" chapter 8 by Derek Wyatt. It mostly treats the Scala version, but I believe that Actor fault handling is implemented in Scala (with dependencies on Java).
Looking in the 2.3.11 sources, the default Actor fault handling decider is in akka-actor_2.11-2.3.11-sources.jar\akka\actor.FaultHandling.scala and is:
/**
* When supervisorStrategy is not specified for an actor this
* [[Decider]] is used by default in the supervisor strategy.
* The child will be stopped when [[akka.actor.ActorInitializationException]],
* [[akka.actor.ActorKilledException]], or [[akka.actor.DeathPactException]] is
* thrown. It will be restarted for other `Exception` types.
* The error is escalated if it's a `Throwable`, i.e. `Error`.
*/
final val defaultDecider: Decider = {
case _: ActorInitializationException ⇒ Stop
case _: ActorKilledException ⇒ Stop
case _: DeathPactException ⇒ Stop
case _: Exception ⇒ Restart
}
/**
* When supervisorStrategy is not specified for an actor this
* is used by default. OneForOneStrategy with decider defined in
* [[#defaultDecider]].
*/
final val defaultStrategy: SupervisorStrategy = {
OneForOneStrategy()(defaultDecider)
}
This agrees with the documentation - more and less insofar as defaultDecider does not include an explicit
case _ => Escalate // _ is the scala wildcard for anything
which is implemented in makeDecider functions that default to Escalate if a Throwable is not matched by defaultDecider; and the documentation does not mention inclusion of a provision for DeathPactException probably to reduce confusion with a detail.