Search code examples
akkaactor

exception during child actor message processing doesn't get logged if parent actor is stopped


Consider below code sample. Here parent actor (ActorA) sends message to child actor (ActorB) and then stops self. Child actor picks up the message and then throws exception. I was expecting some exception logging by actor system but there is no exception logging (see sample output 1).

I understand this is happening due to stopping of parent actor and if I don't stop parent, then exception is logged (see sample output 2).

import akka.actor.Actor
import akka.actor.ActorSystem
import akka.actor.Props
import akka.actor.OneForOneStrategy
import akka.actor.SupervisorStrategy.Stop

object AkkaTest extends App {

  ActorSystem("AkkaTest").actorOf(Props[ActorA]) ! 3

}

class ActorA extends Actor {

  def receive = {
    case i: Int => {
      context.actorOf(Props[ActorB]) ! i 
      context.stop(self)
    }
  }

  override val supervisorStrategy = OneForOneStrategy() {
    case throwable: Throwable  => {
      println("ActorA - supervision strategy - exception in child actor")
      Stop
    }
  }

  override def preStart = println("ActorA - started")

  override def postStop = println("ActorA - stopped")

  override def preRestart(th: Throwable, msg: Option[Any]) = println("ActorA - restarted")

}

class ActorB extends Actor {

  def receive = {
    case i: Int => {
      println("ActorB - processing msg - " + i)
      throw new Exception("boom")
    }
  }

  override def preStart = println("ActorB - started")

  override def postStop = println("ActorB - stopped")

  override def preRestart(th: Throwable, msg: Option[Any]) = println("ActorB - restarted")

}

sample output 1

ActorA - started
ActorB - started
ActorB - processing msg - 3
ActorB - stopped
ActorA - stopped

sample output 2

ActorA - started
ActorB - started
ActorB - processing msg - 3
ActorA - supervision strategy - exception in child actor
ActorB - stopped
[ERROR] [09/09/2014 10:43:08.079] [AkkaTest-akka.actor.default-dispatcher-5] [akka://AkkaTest/user/$a/$a] boom
java.lang.Exception: boom
    at ActorB$$anonfun$receive$2.applyOrElse(AkkaTest.scala:42)
    at akka.actor.Actor$class.aroundReceive(Actor.scala:465)
    at ActorB.aroundReceive(AkkaTest.scala:37)
    at akka.actor.ActorCell.receiveMessage(ActorCell.scala:516)
    at akka.actor.ActorCell.invoke(ActorCell.scala:487)
    at akka.dispatch.Mailbox.processMailbox(Mailbox.scala:238)
    at akka.dispatch.Mailbox.run(Mailbox.scala:220)
    at akka.dispatch.ForkJoinExecutorConfigurator$AkkaForkJoinTask.exec(AbstractDispatcher.scala:393)
    at scala.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260)
    at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339)
    at scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979)
    at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107)

Am I missing any actor system configuration because of which child actor exception is not getting logged when parent is stopped? Is it possible to guarantee logging of any child actor exception in above scenario?


Solution

  • Stopping an Actor "just stops it", it's impossible to know if there's a message in flight from the child (you don't wanna go there). So to answer your question - either gracefully shutdown your children, or be prepared to miss messages like this when stopping an actor.

    If you really want to make always sure to log this message - try {} catch {} is still available for you and it's not bad style or anything to use it where appropriate.

    --- edit: graceful stop example

    // parent
    child ! PleaseDie
    
    
    // child
    case PleaseDie => context stop self