In Akka Cookbook by Héctor Veiga Ortiz, the reader is told that
When an actor throws an exception, it sends a message to the supervisor, and the supervisor handles the failure by restarting that actor. It clears out the accumulated state of the actor, and creates a fresh new actor, means, it then restores the last value assigned to the state of old actor to the preRestart value.
However, I tried testing the following code, which suggests that what the author says isn't true.
import akka.actor._
import akka.actor.SupervisorStrategy._
import akka.util.Timeout
import scala.concurrent.Await
import scala.concurrent.duration._
import akka.pattern.ask
case object Error
case class StopActor(actorRef: ActorRef)
case object Inc
class LifeCycleActor extends Actor {
var sum = 1
override def preRestart(reason: Throwable, message: Option[Any]):Unit =
println(s"sum in preRestart is $sum")
override def preStart(): Unit = println(s"sum in preStart is $sum")
def receive = {
case Inc => sum += 1
case Error => throw new ArithmeticException()
case _ => println("default msg")
}
override def postStop(): Unit =
println(s"sum in postStop is ${sum * 3}")
override def postRestart(reason: Throwable): Unit = {
sum = sum * 2
println(s"sum in postRestart is $sum")
}
}
class Supervisor extends Actor {
override val supervisorStrategy = OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 1 minute){
case _: ArithmeticException => Restart
case t =>
super.supervisorStrategy.decider.applyOrElse(t, (_:Any)=>Escalate)
}
def receive = {
case (props: Props, name: String) => sender ! context.actorOf(props, name)
case StopActor(actorRef) => context.stop(actorRef)
}
}
object ActorLifeCycle extends App {
implicit val timeout = Timeout(2 seconds)
val actorSystem = ActorSystem("Supervision")
val supervisor = actorSystem.actorOf(Props[Supervisor], "supervisor")
val childFuture = supervisor ? (Props(new LifeCycleActor), "LifeCycleActor")
val child = Await.result(childFuture.mapTo[ActorRef], 2 seconds)
child ! Inc
child ! Error
Thread.sleep(1000)
supervisor ! StopActor(child)
}
The output I get is as follows.
sbt:chpt2_ActorLifeCycle> runMain ActorLifeCycle
sum in preStart is 1
sum in preRestart is 2
[ERROR] [11/08/2018 20:06:01.423] [Supervision-akka.actor.default-dispatcher-4] [akka://Supervision/user/supervisor/LifeCycleActor] null
java.lang.ArithmeticException
sum in postRestart is 2
sum in postStop is 6
If what the author says is true, the final value should be double what it is.
First I guess you forget to add
sum += 1
when receive messageInc
in child actor when post the question, please modify. Otherwise, as I test, you cannot get your output.Next explain your code:
From next diagram, you can see the
preReStart
is called on old instance, not on new instance.There is also a desc about this, detail here
So, for your example:
preRestart
, it print the sum of old actor, that is2
, notice: you have inc it.postRestart
, it print the sum of new actor, that is the initial value1
with the calculationsum = sum * 2
, finally print2
, not4
. TheInc message
just receive on old instance, not on new instance.Finally, the content of the book:
I think what you concern is
it then restores the last value assigned to the state of old actor to the preRestart value
. I don't quite know what it means, if you just think it assign the last value of old actor topreRestart
function, then it is correct, as it just run on old instance, otherwise, it seems conflict with akka official guide & experiment; And, if want to restore the value, we may had to useresume
notrestart
. Anyway, I think we should use the akka offical document as a standard, and understand the correct logic.