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:
- I need to take advantage of wildcard selection in actor paths for some reason.
- 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 NewOrderActor
s 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 Tell
ing messages to IActorRef
s, not to ActorSelection
s. 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 IActorRef
s 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 ActorSelection
s than IActorRef
s.
Practice 2: Don’t Hardcode Actor Paths In Your Actor Code
If you need to use ActorSelection
s, 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:
- Avoid typos in your
ActorSelection
s, which will be hell to track down later - 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 ActorSelection
s.
Pattern 1: “Shared ActorPath
s”
The “Shared ActorPaths” pattern is the pattern we just saw, a simple way to reduce errors with ActorSelection
s, often due to hardcoded ActorPath
s.
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 ActorPath
s” 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 ActorSelection
s make sense to you? How have you been using this feature in your applications? Leave a comment below and let me know.
- Read more about:
- Akka.NET
- Case Studies
- Videos
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.