2

I've made following code:

package com.star.wars

import akka.actor._
import akka.actor.SupervisorStrategy._
import akka.util.duration._

object Test extends App {

  case object Kill
  case object Create

  class Luke extends Actor {

    var x: Int = 0

    println("Luke here")
    Thread.sleep(1000)
    println("Luke here2")

    def receive = {
      case Kill => 1/0// context.stop(self)
      case msg: String => {
        x += 1
        println(x + msg)
      }
    }

  }

  class Vader extends Actor {

    println("Vader here")

    override val supervisorStrategy =
      OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 1 minute) {
        case _: ArithmeticException      => Restart
        case _: NullPointerException     => Restart
        case _: IllegalArgumentException => Restart
        case _: Exception                => Restart
      }

    def receive = {
      case Create => context.actorOf(Props(new Luke), name = "Luke")
      case Kill => {
        val luke = context.actorFor("/user/Vader/Luke")
        luke ! "Pre hi there"
        luke ! Kill
        luke ! "Post hi there"
        println("Pre -> Kill -> Post sent to Luke")
      }
    }

  }

  val system = ActorSystem("MySystem")
  val vader = system.actorOf(Props(new Vader), name = "Vader")
  vader ! Create
  vader ! Kill
  println("Create -> Kill sent to Vader")

}

Purpose of this code, is to prove, that while Luke is restarting, his dead letter box can receive messages, and when Luke comes online again, he can receive messages sent to him while he was absent.

Output seems ok. It is a proof in a way:

Create -> Kill sent to Vader
Vader here
Luke here
Pre -> Kill -> Post sent to Luke
Luke here2
1Pre hi there
[ERROR] [01/12/2015 00:32:02.74] [MySystem-akka.actor.default-dispatcher-3] [akka://MySystem/user/Vader/Luke] / by zero
java.lang.ArithmeticException: / by zero
    at com.sconysoft.robocode.Test$Luke$$anonfun$receive$1.apply(test.scala:21)
    at com.sconysoft.robocode.Test$Luke$$anonfun$receive$1.apply(test.scala:20)
    at akka.actor.Actor$class.apply(Actor.scala:318)
    at com.sconysoft.robocode.Test$Luke.apply(test.scala:12)
    at akka.actor.ActorCell.invoke(ActorCell.scala:626)
    at akka.dispatch.Mailbox.processMailbox(Mailbox.scala:197)
    at akka.dispatch.Mailbox.run(Mailbox.scala:179)
    at akka.dispatch.ForkJoinExecutorConfigurator$MailboxExecutionTask.exec(AbstractDispatcher.scala:516)
    at akka.jsr166y.ForkJoinTask.doExec(ForkJoinTask.java:259)
    at akka.jsr166y.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:975)
    at akka.jsr166y.ForkJoinPool.runWorker(ForkJoinPool.java:1479)
    at akka.jsr166y.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:104)

Luke here
Luke here2
1Post hi there

However, I am not sure is it always truth. I can't find it in akka documentation, so is my reasoning ok ? Is dead letter box reusable in akka (after actor restart) ?

Btw. How can I handle Luke's context.stop(self) in Vader's supervisorStrategy ?

Scony
  • 4,068
  • 1
  • 16
  • 23

1 Answers1

2

This is not connected to dead letters, see Message Delivery Reliability. In Akka, when using In-JVM-messaging - you have guarantee that message will be delivered with high-probability in most cases (restarting after exception is one of those). Messages that could not be delivered (like messages to voluntary stopped or never existed actor) are going to the DeadLetters box, but you should subscribe to them explicitly, which is not you've got here. You had just received the messages from your own actor's mailbox (as box wasn't removed during restarting - only actor's instance). You need to explicitly subscribe to the corresponding Event Stream to watch deadLetters.

You can't process context.stop(self) inside supervisorStrategy as it's voluntary termination (which actually will cause messages going to deadLetters) not exceptional situation (failure). So 1/0 and context.stop(self) are very different. For listening child's lifecycle see - What Lifecycle Monitoring Means

For example, let's see what if you really put context.stop(self) into the code instead of 1/0:

Luke here
Pre -> Kill -> Post sent to Luke
Luke here2
1Pre hi there
[INFO] [01/12/2015 09:20:37.325] [MySystem-akka.actor.default-dispatcher-4] [akka://MySystem/user/Vader/Luke] Message [java.lang.String] from Actor[akka://MySystem/user/Vader#-1749418461] to Actor[akka://MySystem/user/Vader/Luke#-1436540331] 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'.

So that's where ("blabla was not delivered" in logging) deadLetters mailbox is used.

Anyway, any delivery (including dead-letters as it's just a synthetic actor) is based on best effort principle for in-JVM messaging, so there is no absolute guarantees:

The Akka test suite relies on not losing messages in the local context (and for non-error condition tests also for remote deployment), meaning that we actually do apply the best effort to keep our tests stable. A local tell operation can however fail for the same reasons as a normal method call can on the JVM:

  • StackOverflowError
  • OutOfMemoryError
  • other VirtualMachineError

In addition, local sends can fail in Akka-specific ways:

  • if the mailbox does not accept the message (e.g. full BoundedMailbox)
  • if the receiving actor fails while processing the message or is already terminated

While the first is clearly a matter of configuration the second deserves some thought: the sender of a message does not get feedback if there was an exception while processing, that notification goes to the supervisor instead. This is in general not distinguishable from a lost message for an outside observer.

In case of network-messaging you have no delivery guarantee at all. Luke could never know who is his father. Why? Because it's faster and actually Nobody Needs Reliable Messaging:

The only meaningful way for a sender to know whether an interaction was successful is by receiving a business-level acknowledgement message, which is not something Akka could make up on its own (neither are we writing a “do what I mean” framework nor would you want us to).

dk14
  • 22,206
  • 4
  • 51
  • 88