3
votes

I would like to know how to efficiently cleanup akka actors that are created on the fly.

To give a bit of background:

Actor Hierarchy created per event.

Supervisor -> child1 -> grandChild1

In my application the supervisor actor dynamically creates other actors(on a periodic event). I wanted to cleanup the actors after the processing steps for that event is complete.

So, I would like to kill all the child actors once the processing is complete.

  1. I am propagating a message (successfulProcessing) after successful completion in the reverse of creation. (Grandchild1 -> child1 -> Supervisor ). In the Supervisor, I will send a PoisonPill to the child actor.

This is the code for the Supervisor actor.

class Supervisor extends Actor {
    def receive={
        case onEvent: OnEvent =>
            //Create child actor and send message         
        case successfulProcessing =>
            sender() ! PoisonPill
    }
    override val supervisorStrategy = AllForOneStrategy() {
        case e: Exception =>
            Stop   
    }
}

Is this the correct approach to cleanup the dynamically created actors. If there is any disadvantage to this approach or is there a pattern to be followed?

2
if the supervisor doesn't do anything else, the child can kill itself without sending a message to its parent. - Giovanni Caporaletti
@GiovanniCaporaletti But, say if the processing of child1 completes and grandchild1 is still ongoing, if I kill the child1, wouldn't that create problems? - Maximus
when you kill child1 you kill all its children too - Giovanni Caporaletti
PoisonPill is a event. It waits in queue until last message is not processed. So the Actor first finish processing current message and next it will send PoisonPill to the children and stop himself. - Andrzej Jozwik

2 Answers

1
votes

As per Akka Document 2.4.14 , Better way to handle PoisonPill/Kill message is to broadcast them.

    ActorRef ! Broadcast(PoisonPill)

Note: Do not broadcast messages when using BalancingPool

0
votes

The pattern I've seen is to have an actor who manages other actors. In the following example from this tutorial, actor1 manages actor2, where actor2 does all the work. actor1 then cleans up.

case class StartCounting(n: Int, actor: ActorRef)
case class CountDown(n: Int)

class CountDownActor extends Actor {
  def receive = {
    case StartCounting(n, actor) =>
      println(n)
      actor ! CountDown(n-1)
    case CountDown(n) =>
      if(n > 0) {
        println(n)
        sender ! CountDown(n-1)
      } else {
        context.system.shutdown()
      }
  }
}

object Main extends App {
  val system = ActorSystem("HelloSystem")
  // default Actor constructor
  val actor1 = system.actorOf(Props[CountDownActor], name = "manager")
  val actor2 = system.actorOf(Props[CountDownActor], name = "worker")
  actor1 ! StartCounting(10, actor2)
}

You can think of this like recursion: base and inductive cases. You can apply this at depth for all sibling actors being managed their parent.