Actor Hierarchy

Actors in Riker form a hierarchy with each actor addressable by a path. An actor's place in the hierarchy is determined by the location of its parent. Let's take a look at what the actor hierarchy looks like immediately after the actor system has started:

  1. my-app
  2. └─ user
  3. └─ system
  4. └─ logger
  5. └─ event_stream
  6. └─ dead_letters
  7. └─ event_store
  8. └─ default_stream
  9. └─ io_manager
  10. └─ tcp
  11. └─ dl_logger
  12. └─ temp

We can see that without starting any actors ourself there's already a number of actors running. At the base of the hierarchy is our application root which has the name provided when the system was started: ActorSystem::new("my-app").

There's then three root actors, user, system and temp. Perhaps the most important of these is user, since most actors created as part of the application are created in this branch.

If we start an actor using system.actor_of(props, "my-actor") we can see it added under user:

  1. my-app
  2. └─ user
  3. └─ my-actor <-- our new actor is added
  4. └─ system
  5. └─ logger
  6. └─ event_stream
  7. └─ dead_letters
  8. └─ event_store
  9. └─ default_stream
  10. └─ io_manager
  11. └─ tcp
  12. └─ dl_logger
  13. └─ temp

In this case the newly created my-actor has a path of /user/my-actor. Since it was started by using actor_of on ActorSystem it is considered a top-level actor.

Let's look at how the hierarchy changes when another actor is started, this time from within /user/my-actor's receive method using Context.actor_of.

  1. impl Actor for MyActor {
  2. type Msg = String;
  3.  
  4. fn receive(&mut self,
  5. ctx: &Context<Self::Msg>,
  6. msg: Self::Msg,
  7. sender: ActorRef<Self::Msg>) {
  8.  
  9. ctx.actor_of(MyActor::props(), "my-child").unwrap();
  10. }
  11. }

Here MyActor will start another actor, which is also an instance of MyActor.

  1. my-app
  2. └─ user
  3. └─ my-actor
  4. └─ my-child <-- our new actor is added
  5. └─ system
  6. └─ logger
  7. └─ event_stream
  8. └─ dead_letters
  9. └─ event_store
  10. └─ default_stream
  11. └─ io_manager
  12. └─ tcp
  13. └─ dl_logger
  14. └─ temp

Since the new actor was started using my-actor's context it gets added to the hierarchy as a child of my-actor. my-child's path becomes /user/my-actor/my-child.

Let's move on the next section where the importance of the actor heirarchy to achieve supervision is made clear when building resilient applications.