Question

What will happen, when I schedule a message in the constructor of my Actor and the actor fails (Exception) before the message was send?

  • When the actor is resumed or restarted, will the message send like nothing has happened?
  • When the actor is stopped, will the message send to the dead letter box?
  • When I start the timer in preStart(), will I have two scheduled message when the actor restarts after a failure?
Was it helpful?

Solution

The answers to your questions are as follows:

1) Yes, the actor will receive the message as long as you used the scheduler scheduleOnce variant that takes an ActorRef as an arg. Because an ActorRef is just a lightweight proxy based on an actor address, it can survive failures of the target actor and still route messages to it as long as it successfully restarts back up and re-binds to the address that the ActorRef represents/

2) Yes, if the ActorRef is for a path that is no longer represented in the ActorSystem then the message will be sent to deadletter instead.

3) Yes you will. If you do it in preStart or in the body of the actor (constructor) and the actor fails and restarts, then the scheduled will now have two jobs to do for the same ActorRef and thus two requests will eventually be received.

A little code to show all of this in action. Consider the following actor:

class TestSchedActor extends Actor{
  import context._

  override def preStart = {
    context.system.scheduler.scheduleOnce(1 second, self, "bar")  
  }      

  def receive = {

    case "foo" =>
      val s:String = null
      s.length

    case "baz" =>
      context stop self

    case other =>
      println(s"Got $other")
  }
}

If you tested it in this way:

  val system = ActorSystem("schedtest")
  val ref = system.actorOf(Props[TestSchedActor])
  ref ! "foo"

Then the output would be:

[ERROR] [04/03/2014 07:58:24.988] [schedtest-akka.actor.default-dispatcher-2] [   akka://schedtest/user/$a] null
java.lang.NullPointerException
at code.TestSchedActor$$anonfun$receive$1.applyOrElse(Asking.scala:27)
at akka.actor.ActorCell.receiveMessage(ActorCell.scala:498)
at akka.actor.ActorCell.invoke(ActorCell.scala:456)
at akka.dispatch.Mailbox.processMailbox(Mailbox.scala:237)
at akka.dispatch.Mailbox.run(Mailbox.scala:219)
at akka.dispatch.ForkJoinExecutorConfigurator$AkkaForkJoinTask.exec(AbstractDispatcher.scala:386)
at scala.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:262)
at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:975)
at scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1478)
at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:104)

Got bar
Got bar

This shows both #1 and #3 as the actor still received the message after failure and actually received 2 as it re-scheduled again when it was restarted.

If you tested the actor like this:

  val system = ActorSystem("schedtest")
  val ref = system.actorOf(Props[TestSchedActor])
  ref ! "baz" 

Then you would see the following output:

[INFO] [04/03/2014 08:01:14.199] [schedtest-akka.actor.default-dispatcher-2] [akka://schedtest/user/$a] Message [java.lang.String] from 
Actor[akka://schedtest/user/$a#687201705] to Actor[akka://schedtest/user/$a#687201705] was 
not delivered. [1] dead letters encountered. This logging can be turned off or adjusted 
with configuration settings 'akka.log-dead-letters' and 'akka.log-dead-letters-during-shutdown'.

Provided you had not disabled deadletter logging.

OTHER TIPS

I assume that your actor sends a message (using a scheduled task) to itself (using something like system.actorSelection to resolve self address). Then: 1) Yes; 2) Yes; 3) Yes (moreover, if you start the timer in the constructor you'll get the same behavior).

To avoid all such issues you can start the timer in preStart(), save the received Cancellable into a local variable inside the Actor and then cancel it in postStop(). postStop() / preStart() are called from preRestart() / postRestart(), so your scheduled task will be rescheduled on Actor restarts and cancelled on Actor stop.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top