Search code examples
javaactorakka

How to know if an actor is idle


I have an unknown number of jobs to be executed by a known (of course) number of actors. The initial jobs number might increase after an actor completes a job. That is, an actor, upon completion of its task, might add a new job to be executed.

The way I am handling this is to have each actor send a message back to the master when it completes its job, not only with the result of the execution but also with a 'flag' indicating that the actor is now idle. The master has a queue of jobs and a queue of idle actors, whenever an actor sends a 'job completed message' the master will check if there's something else for that actor to do... so on and so forth until the jobs' queue is empty and the idles' queue is full... at that point I shutdown the system. There's no much supervision here, so I feel that I am not doing it properly...

I am not using a router because I could not find a way to query the router for idle actors, so my question is:

What is the 'proper' way to handle the situation I have described above in Akka?


Solution

  • You should take a look at Akka's routing capabilites. SmallestMailboxRouter might be what you are looking for.

    As an alternative, you could just create actors on demand, i.e. for every task, a new actor is created dynamically. A central actor keeps track of all the actors that are currently active. Once a worker actor is done, it sends itself a PoisonPill and informs the master about its shutdown (actively, or via the standard Terminate message that Akka will send to the supervising actor). Once there are no more active actors, i.e. no more tasks, the controler actor shuts down the system.

    Addition after reading the comment: Take a look at the sources of SmallestMailboxLike, a Scala trait mixed in by SmallestMailboxRouter. Warning: you should have a basic knowledge of Scala. But this is generally a good idea anyway if you want to use Akka... The method isProcessingMessage(ActorRef) can be understood as isNotIdle(ActorRef)

    // Returns true if the actor is currently processing a message.
    // It will always return false for remote actors.
    // Method is exposed to subclasses to be able to implement custom
    // routers based on mailbox and actor internal state.
    protected def isProcessingMessage(a: ActorRef): Boolean = a match {
      case x: LocalActorRef ?
        val cell = x.underlying
        cell.mailbox.isScheduled && cell.currentMessage != null
      case _ ? false
    }
    
    // Returns true if the actor currently has any pending messages
    // in the mailbox, i.e. the mailbox is not empty.
    // It will always return false for remote actors.
    // Method is exposed to subclasses to be able to implement custom
    // routers based on mailbox and actor internal state.
    protected def hasMessages(a: ActorRef): Boolean = a match {
      case x: LocalActorRef ? x.underlying.mailbox.hasMessages
      case _                ? false
    }