Akka Best Practices: Defining Actor Props

No Comments

Akka provides an implementation of the actor model for building reactive applications. So in Akka, an application is made up of actors rather than of plain old objects. When creating actors, we need to pass Props instances. So in this blog post I’m going to show best practices when defining actor Props.

Naive Approach

Unlike Scala objects, Akka actors are created using dedicated factory methods, e.g. ActorContext.actorOf. To create an actor we need to pass an instance of Props and wrap the call to the actor’s constructor inside those Props:

class ParentActor extends Actor {
  val child = context.actorOf(Props(new ChildActor))
  override def receive = Actor.emptyBehavior

Looks straightforward, right? Well, although in the above example the code will work as expected it is easy to introduce subtle bugs when defining actor Props like this. Consider the following slightly more complexe example:

final class ParentActor extends Actor {
  var counter = 0
  override def receive = {
    case Increment   => counter++
    case CreateChild => context.actorOf(Props(new ChildActor(counter)))
final class ChildActor(value: Int) extends Actor {
  override def receive = Actor.emptyBehavior

The ParentActor now accepts two messages:

  • Increment which will increment value of the internal mutable variable counter.
  • CreateChild which creates a new ChildActor passing the value of counter to its constructor.

As we know Akka takes care of concurrency when executing the current receive behavior. So we would expect that the constructor of ChildActor will print the value of the counter at the time the CreateChild message was received. However, this may not always be the case, as I’m going to show next.

The Problem: Closing over the enclosing actor’s state

In the code above we have introduced a Heisenbug because we’re closing over the ParentActor's state. This is because of the way the apply method is defined in the Props companion object. Let’s have a look:

def apply[T <: Actor: ClassTag](creator: => T): Props

As you can see the creator parameter is passed by-name and not by-value. As we recall, passing a parameter by name means that it is only evaluated when it is accessed inside the method body. So instead of calling the constructor of ChildActor and passing the result to Props.apply, the constructor call is passed and evaluated later.

Why is this a problem? You can think of the creator parameter as a function with zero arity, which returns as value of type T. Inside this closure, we defined a reference to the mutable variable counter. The closure will be evaluated by Akka asynchronously and it is not specified whether ParentActor has already received new Increment messages in the meantime, changing the value of counter.

The Solution: Props Factory in the Companion Object

So what’s the solution to our problem? We need to make sure that we’re not closing over the ParentActor's state when creating Props for ChildActor. How can we achieve this? By making sure all constructor values are accessed by-value, before passing them to Props.apply. Since Props always belong to a specific actor implementation, it makes sense to define a factory method for them in the companion object of our actors:

object ChildActor {
  final val Name = "child-actor"
  def apply(value: Int): Props = Props(new ChildActor(value))

Note that I also added a constant for the actor name. When defining constants final and starting with an upper case letter, the Scala compiler will inline them. So this is another small best practice when implementing actors. Creating a ChildActor now looks like this:

 final class ParentActor extends Actor {

var counter = 0

  • Page
  • 1
  • 2

Benedikt Ritter works as a Software Craftsman at codecentric AG in Solingen since September 2013. His joy for creating reliable software is not limited to coding at work: Benedikt is member of the Apache Software Foundation and Committer for the Apache Commons project.

Share on FacebookGoogle+Share on LinkedInTweet about this on TwitterShare on RedditDigg thisShare on StumbleUpon


Your email address will not be published. Required fields are marked *