October 22, 2014 · actorpath akka

Akka Notes - Child Actors and ActorPath - 6

Actors are completely hierarchical. Whatever Actors that you create HAS to be a child of some other Actor.

Let's analyze that a bit :

Path

Say, we create an ActorRef using ActorSystem.actorOf and try to print its path.

val actorSystem=ActorSystem("SupervisionActorSystem")
val actorRef=actorSystem.actorOf(Props[BasicLifecycleLoggingTeacherActor])
println (actorRef.path) // (prints) akka://SupervisionActorSystem/user/$a

As you see, a path looks very similar to a file path in a file system.

  1. akka here is fixed because all these are addresses of Akka Actors - more like file:// or http:// prefix (nothing to do with protocol though).
  2. The SupervisionActorSystem is just the name of your ActorSystem that you created.
  3. We'll talk about the user in the next section.
  4. The $a is the name of your Actor that the system generated for you. How would you like if your operating system generated random file names for your files? You'd obviously hate it because you would want to refer to that name in future. So, let's give it a proper meaningful name to it :

val actorRef=actorSystem.actorOf(Props[BasicLifecycleLoggingTeacherActor], "teacherActor")
println (actorRef.path)     // (prints) akka://SupervisionActorSystem/user/teacherActor

That's it. Now, the path actually makes sense.

Top Level Actor

Child Actors

Similar to top level actors which we create out of ActorSystem, we could also create child actors out of the ActorContext. In fact, the power of Actors's fault tolerance primarily lie within leveraging the Actor hierarchy and the ability of a parent to manage the life of child actors.

Assume you have a TeacherSupervisor and you would like to create a TeacherActor to be a child of the Supervisor, you do a ActorContext.actorOf instead of a ActorSystem.actorOf:

class TeacherSupervisor extends Actor with ActorLogging {
  val teacherActor=context.actorOf(Props[TeacherActor], "teacherActor")
...
...

Frankly, in any application, you will be creating a whole lot of child actors than top level actors - which means that you'll be calling a lot more actorContext.actorOf than actorSystem.actorOf.

Parent and Child Actor

You'll notice that the path of the child actor is akka://SupervisionActorSystem/user/teacherSupervisor/teacherActor, which is very similar to the path you get when you create a child folder within a parent folder.

When do you create child Actors?

You generally create child actors when a particular task is composed of a subtask or multiple subtasks. You also create a child actor when a particular task to be executed by the parent is error-prone and you would want to isolate it (so that if it fails, you could recover). When there is no parent child relationship between tasks, then you DON'T create child actors.

Also, there's nothing which is stopping a child actor from creating children to delegate its subtasks. Actors and their creation are really cheap but the power that comes with it is amazing (we'll see about this while we talk about supervision).

Now what is that user in the path?

For lack of a creativity, let me compare the ActorSystem to a Unix file system - with a / root folder and all those /etc, /usr, /bin and various other folders.

ActorSystem are much like that. It creates a few top level Actors - the most important being the root Actor with the path /, the user Actor with the path /user and a system Actor with the path /system. (there's also a /deadLetters that represent the DeadLetterActorRef. We saw this in our previous post)

Codewise, the ActorSystem composes three Actors inside it (via ActorRefProvider). Those are the root for ALL the Actors that gets created under the ActorSystem.

  1. systemGuardian actor - root of all actors under /system
  2. guardian actor - root of all actors under /user and
  3. rootGuardian Actor - root of both the systemGuardian and the userGuardian actors.
  /**
   * Reference to the supervisor of guardian and systemGuardian; ....
   */
  def rootGuardian: InternalActorRef

  /**
   * Reference to the supervisor used for all top-level user actors.
   */
  def guardian: LocalActorRef

  /**
   * Reference to the supervisor used for all top-level system actors.
   */
  def systemGuardian: LocalActorRef

Root System and user guardians

/user (aka) user guardian

Any Actor that you create in your program like the StudentActor or the TeacherActor using the ActorSystem's actorOf method would directly fall under /user. That's the reason your teacherActor in the first part of this write-up had the path slug /user/teacherActor.

/system (aka) system guardian

The system guardian shuts itself down when it notices that the userGuardian is dead. Makes sense considering if the userGuardian is down, then all the business actors under it is also down and therefore all administrative actors needs to be gone too.

We could see two distinct places where System Actors are being created - I mean actors under the /system hierarchy.

  1. Like we saw earlier, any message that you send to an Actor that is terminated gets forwarded to the mailbox of an internal Actor called DeadLetterActor. The DeadLetter Actor wraps each message as a DeadLetter and publishes it to the EventStream. One other Actor called DeadLetterListener consumes all DeadLetters and publishes that as a log message. Now, the DeadLetterListener is a system Actor with path /system/deadLetterListener.

  2. Remember the TestEventListener that we created in our previous write-up to subscribe to log messages in the EventStream? They are System actors too. In fact, all akka.loggers are created as System actors.

class TeacherTest extends TestKit(ActorSystem("UniversityMessageSystem", ConfigFactory.parseString("""akka.loggers = ["akka.testkit.TestEventListener"]""")))
...
...

The documentation here says that any Actor that is configured in the configuration files and created and deployed into the ActorSystem while it starts, falls under the /system umbrella. Let me update this post when I find something interesting around this

/ (aka) root guardian

As we saw earlier, the / Actor is the parent of the user and the system guardians.


TRIVIA

Technically, there's a superfluous parent for the root actor too. This Actor's only job is to shut down the entire ActorSystem if the root actor fails. Since it's strictly not considered within the Actor hierarchy, the Akka team calls it :

  private[akka] val theOneWhoWalksTheBubblesOfSpaceTime: InternalActorRef = new MinimalActorRef {
...