0
votes

I want to avoid dead letters message to a terminated actor and avoid sending message to that actor

class PingActor extends Actor with ActorLogging {
  import PingActor._

  var counter = 0
  var sendMessages = true
  val pongActor = context.actorOf(PongActor.props, "pongActor")
  context.watch(pongActor)

  def receive = {
    case Terminated(pong) =>
      sendMessages = false


    case Initialize =>
        println("In PingActor - starting ping-pong")
      pongActor ! PingMessage("ping")
    case PongActor.PongMessage(text) =>
      println("In PingActor - received message: {}", text)
      counter += 1
      if (counter == 10 ) context.system.shutdown()
      else {
        context.actorSelection(pongActor.path) ! PingMessage("ping")
      }
  } 
}
class PongActor extends Actor with ActorLogging {
  import PongActor._
  var counter = 0

  def receive = {
    case PingActor.PingMessage(text) => 
      println(s"In PongActor - received message: $text counter = $counter \n reply with pong message")

      if (counter < 5) {
        counter = counter + 1
      }
      else
        {
          println("Oh crap , bye bye ")
          context.stop(self)
        }
      sender() ! PongMessage("pong")

  }

the actorSelection did not work as I expected , and the last message sent from ping still ends to dead letters :

[INFO] [09/16/2016 00:47:46.237] [MyActorSystem-akka.actor.default-dispatcher-4] [akka://MyActorSystem/user/pingActor/pongActor] Message [com.example.PingActor$PingMessage] from Actor[akka://MyActorSystem/user/pingActor#1697177867] to Actor[akka://MyActorSystem/user/pingActor/pongActor#524615423] 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'.

2

2 Answers

1
votes

Based on the async nature of actors I don't think you can relay on ActorSelection for that. I think once you have received a Terminated message you are certain the actor is dead. However, there might still be few messages in the mailbox waiting to be processed when the actor dies. What you can do is to have another actor listening on the DeadLetter channel and act if that actor gets a message that was meant to go to some other actor.

0
votes

Another alternative to @hveiga answer could be:

To only send the next ping message after you receive the pong from pongActor. This way you could control if you should send them or not. But then if the load is high, it would be a high bottle neck.

var buffer = 0;
def receive = {
    case Terminated(pong) =>
      sendMessages = false

    case Initialize =>
      if(buffer == 0){
          self ! SendMsg
          buffer = buffer+1;
       } else buffer = buffer +1;
    case SendMsg => 
          println("In PingActor - starting ping-pong")
          pongActor ! PingMessage("ping")

    case PongActor.PongMessage(text) =>
         buffer = buffer -1;
         if(buffer > 1) self ! SendMsg
         .....
  } 

Instead of handling it manually, you could also stash them. So that the subsequent Initialize are not processed dill the time you receive PongMessage from pongActor

A better design could be: That you keep sending messages and keep checking for response say every 5 seconds or so. If the PongActor didn't reply back in that duration, it probably means it is dead. And you would have a list on the messages for which there was no response. So the accountability is taken care of.