Search code examples
scalaasynchronousakkaactor

Akka actor - sender points to dead letters


consider the following example:

case class Payload(message: String, async: Boolean)

class EchoActor extends Actor {
  override def receive: Receive = {
    case Payload(message, async) =>
      if (async) Future {
        println(s"from: $sender")
        sender ! message
      } else {
        println(s"from: $sender")
        sender ! message
      }
  }
}

def main(args: Array[String]): Unit = {
  val system = ActorSystem("demo")
  val echo = system.actorOf(Props[EchoActor])
  implicit val timeout = Timeout(2 seconds)

  (echo ? Payload("Hello", async = false)).mapTo[String].foreach(println(_))
  (echo ? Payload("Async Hello", async = true)).mapTo[String].foreach(println(_))

  StdIn.readLine()
  system.terminate()
}

console output:

from: Actor[akka://demo/temp/$a]
Hello
from: Actor[akka://demo/deadLetters]
[INFO] [04/13/2017 19:56:58.516] [demo-akka.actor.default-dispatcher-4] [akka://demo/deadLetters] Message [java.lang.String] from Actor[akka://demo/user/$a#2112869650] to Actor[akka://demo/deadLetters] 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'.

i.e. the sender points to deadLetters when accessing it from another thread.

What is the reason behind that? is it a bug?

although, we can hold a reference to the actual sender to make it work:

if (async) {
  val currentSender = sender()
  Future {
    println(s"from: $currentSender")
    currentSender ! message
  }
}

but... isn't there some better approach?


Solution

  • This is not a bug, but a documented behavior -

    http://doc.akka.io/docs/akka/2.5.0/scala/actors.html#Send_messages

    Using Future means invoking an anonymous function which is not an instance of the Actor class, therefor your sender() ref is mapped to the deadLetters mailbox