0
votes

I have supervisor actor which selects child actor based on command received, whenever it creates a new child actor and sends message(ask pattern) it timesout as child actor tries to send back response but it goes to dead letters.

Here is the child actor code

class IdActor(id: String, injector: Injector) extends Actor {
  override def receive: Receive = {
    case cmd: GenerateIdCmd =>
      val parent = sender()
      ...
      Future(idEvent) pipeTo sender //Issue is here going to dead letters
      //Future(idEvent) pipeTo parent //This also leads to same problem
      //parent ! idEvent // Same issue
  }
}

Here is supervisor code

class IdSupervisor(injector: Injector) extends Actor {
  override def receive: Receive = {
    case cmd: GenerateIdCmd =>
      ...
      val ref = context.child(cmd.id).getOrElse {
        context.actorOf(Props(classOf[IdActor], cmd.id, injector), cmd.id)
      }
      ask(ref, cmd) pipeTo sender
  }
}

Second message is flowing back to originator, first response from all new child actors going to dead letters from there afterwards going good.

Working Solution Issue is with Supervisor, fixed code

class IdSupervisor(injector: Injector) extends Actor {
      override def receive: Receive = {
        case cmd: GenerateIdCmd =>
          val originator = sender
          ...
          val ref = context.child(cmd.id).getOrElse {
            context.actorOf(Props(classOf[IdActor], cmd.id, injector), cmd.id)
          }
          ask(ref, cmd) pipeTo originator
      }
    }
2

2 Answers

1
votes

Never ever pipe directly to sender from future inside actor, sender is def and at the moment when future is completed, it might be already different one than you expect. One of solution is to store sender before future call:

class IdActor(id: String, injector: Injector) extends Actor {
  override def receive: Receive = {
    case cmd: GenerateIdCmd =>
      ...
      val originalSender = sender
      Future(idEvent) pipeTo originalSender
  }
}
1
votes

I suspect that the issue doesn't actually occur when a child replies to the parent as you describe, but when the parent sends a message to a child and expects a reply:

val ref = context.child(cmd.id).getOrElse {
  context.actorOf(Props(classOf[IdActor], cmd.id, injector), cmd.id)
}
ask(ref, cmd) pipeTo sender

An actor is started asynchronously upon creation. The observation that, when a new child is created, the first message to that child results in dead letters, while subsequent messages to that child result in the intended behavior, suggests an actor initialization issue. What's probably happening is that the child actors receive their first GenerateIdCmd message before they have completely started.