My favorite part about teaching Akka.NET to people is the questions I get. I never have any clue what someone is going to ask me, and that’s kind of exciting.

Last Thursday, we had an awesome group of developers participate in our advanced Akka.NET Design Patterns training for half the day. We covered a ton of material, but in that time, there was a basic question that came up:

When should I use an ActorSelection?

I loved this question, because there is more nuance to the seemingly mundane ActorSelection than meets the eye.

When Should I Use An ActorSelection?

The guideline I follow is to use an ActorSelection when:

  1. I need to take advantage of wildcard selection in actor paths for some reason.
  2. I need to communicate with an actor on a remote actor system, and I don’t have an actor reference for it yet.

Wildcard Routing

The first case when I usually use an ActorSelection is when I need to leverage wildcards in my actor paths. This is pretty rare, but one example could be if I needed to distribute a message to a number of actors who all lived at the same level in the hierarchy, or had a well-defined and predictable ActorPath.

For example, let’s imagine I had an OrderActor and needed to report on new orders to finance, marketing, and fulfillment. Let’s imagine that each of those concerns has its own hierarchy, with a NewOrderActor at the second level that is the ingestion point for new orders. It would look like this:

In this case, I could send the new order messages on to all three NewOrderActors with one wildcard ActorSelection, like so:

// assume actor system named ProductSystem
public class OrderActor : ReceiveActor
   {
       public OrderActor()
       {
           Receive<NewOrderMessage>(order =>
           {
               // tell all three NewOrderActors about new order via wildcard ActorSelection
               Context.ActorSelection("akka://ProductSystem/user/*/NewOrderActor").Tell(order);
           });
       }

   }

This is overly-simplified, but it demonstrates the point.

Initial Remote Messages

The other time when I tend to use an ActorSelection is when I am initially communicating with a remote actor system.

Since I don’t yet have a handle to any actor on that remote system, I just send a message at an address and hoping someone is listening, and will send me back an IActorRef that I can start to collaborate with.

Akka.Remote is the most powerful of all the modules in Akka. It’s what enables location transparency, powers clustering, allows peer-to-peer architectures, and is the foundation of all things distributed within Akka.

If you’re interested in REALLY learning how to use Akka in production, I strongly encourage you to join our next Akka.Remote training course and learn how to design an Akka.NET app for the network using Akka.Remote.

So what are the best practices when I do use an ActorSelection?

ActorSelection Best Practices

Practice 1: Always Use An IActorRef If Possible

Again, you should usually be Telling messages to IActorRefs, not to ActorSelections. With an IActorRef, you know that the actor has existed at some point in time in the past. This is a guarantee of the Akka framework: all IActorRefs have existed at some point, even if the actor is now dead.

With an ActorSelection, you have no such guarantee. It’s kind of like UDP—you’re just firing messages at an address with no idea if anyone is listening. Bugs and errors are also more difficult to nail down with ActorSelections than IActorRefs.

Practice 2: Don’t Hardcode Actor Paths In Your Actor Code

If you need to use ActorSelections, put the paths in a shared class and then have all your other actors reference that class.

If you hardcode your actor paths throughout your code, you are asking for headaches.

There are two good reasons to do this:

  1. Avoid typos in your ActorSelections, which will be hell to track down later
  2. Minimize the cost of changing where actors live in the hierarchy (which you will), so that you only have to make that change in one place and it can flow out from there.

Helpers & Design Patterns Repo

Because sharing is caring, we’ve started a repo where we’ll be adding helper classes and simple patterns for common production needs. Check it out here: Akka.NET Helpers. It’s a bit bare bones today but will grow quickly as the contributors add to it.

There are two patterns from that repo that I want to introduce you to today. These cover the best practices mentioned above, for when you do use ActorSelections.

Pattern 1: “Shared ActorPaths”

The “Shared ActorPaths” pattern is the pattern we just saw, a simple way to reduce errors with ActorSelections, often due to hardcoded ActorPaths.

As I mentioned, the best practice is to store the ActorPaths in this public static class so that other parts of your code can reference it easily. It will also avoid dangling references and hard-to-track down errors when you change an ActorPath. Or type one in wrong.

Here’s what that looks like:

using Akka.Actor;

namespace AkkaHelpers
{
    /// <summary>
    /// Static helper class used to define paths to fixed-name actors
    /// (helps eliminate errors when using <see cref="ActorSelection"/>)
    /// </summary>
    public static class ActorPaths
    {
        public static readonly ActorMetaData AuthenticatorActor = new ActorMetaData("authenticator");
        public static readonly ActorMetaData FormActor = new ActorMetaData("mainform");
        public static readonly ActorMetaData ValidatorActor = new ActorMetaData("validator", AuthenticatorActor);
        public static readonly ActorMetaData NestedValidatorActor = new ActorMetaData("childValidator", ValidatorActor);
    }
}

…and we can then get access to the ActorPath we need for an ActorSelection like so:

Context.ActorSelection(ActorPaths.AuthenticatorActor.Path).Tell(message);

NOTE: The “Shared ActorPaths” pattern should be used in conjunction with…

Pattern 2: Actor MetaData

The “Actor MetaData” pattern is a simple way to add extra information which makes the “Shared ActorPaths” pattern much more useful. This enables you to store metadata about an actor path, letting you easily retrieve the actors name, ActorPath, and parent, even when you don’t have a handle to an actor (e.g. don’t have an IActorRef).

For example:

using Akka.Actor;

namespace AkkaHelpers
{
    /// <summary>
    /// Meta-data class. Nested/child actors can build path
    /// based on their parent(s) / position in hierarchy.
    /// </summary>
    public class ActorMetaData
    {
        public ActorMetaData(string name, ActorMetaData parent = null)
        {
            Name = name;
            Parent = parent;
            // if no parent, we assume a top-level actor
            var parentPath = parent != null ? parent.Path : "/user";
            Path = string.Format("{0}/{1}", parentPath, Name);
        }

        public string Name { get; private set; }
        public ActorMetaData Parent { get; set; }
        public string Path { get; private set; }
    }
}

Be sure to bookmark the Akka.NET Helpers repo and stay tuned for more patterns and best practices coming your way.

We also have a few spots left for our upcoming Akka.Remote and Akka.Cluster training courses, which are designed to help you take advantage of the full distributed capabilities of Akka.NET.

Does this approach to using ActorSelections make sense to you? How have you been using this feature in your applications? Leave a comment below and let me know.

If you liked this post, you can share it with your followers or follow us on Twitter!
Written by Andrew Skotzko on May 20, 2015

 

 

Observe and Monitor Your Akka.NET Applications with Phobos

Did you know that Phobos can automatically instrument your Akka.NET applications with OpenTelemetry?

Click here to learn more.