Beliebte Suchanfragen
//

Akka Best Practices: Defining Actor Props

10.3.2017 | 4 minutes of reading time

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:

1class ParentActor extends Actor {
2 
3  val child = context.actorOf(Props(new ChildActor))
4 
5  override def receive = Actor.emptyBehavior
6}

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:

1final class ParentActor extends Actor {
2 
3  var counter = 0
4 
5  override def receive = {
6    case Increment   => counter++
7    case CreateChild => context.actorOf(Props(new ChildActor(counter)))
8  }
9}
10 
11final class ChildActor(value: Int) extends Actor {
12 
13  println(value)
14 
15  override def receive = Actor.emptyBehavior
16}

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:

1def 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:

1object ChildActor {
2 
3  final val Name = "child-actor"
4 
5  def apply(value: Int): Props = Props(new ChildActor(value))
6}

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:

1final class ParentActor extends Actor {
2 
3  var counter = 0
4 
5  override def receive = {
6    case Increment   => counter++
7    case CreateChild => context.actorOf(ChildActor(counter), ChildActor.Name))
8  }
9}

This way, when we call ChildActor.apply the current value of counter is passed by-value. This makes sure the new ChildActor instance is created with counter at the time the CreateChild message was received.

Summary

When implementing actors we need to make sure that we don’t close over some other actor’s state. Only if that is the case, our code will work reliably. The best way to achieve this is to define a factory method for Props in the actor’s companion object. By passing all actor constructor parameters to that factory method by-name, we can make sure that they reflect the state of the enclosing actor at the time we created the new child actor.

share post

//

More articles in this subject area

Discover exciting further topics and let the codecentric world inspire you.

//

Gemeinsam bessere Projekte umsetzen.

Wir helfen deinem Unternehmen.

Du stehst vor einer großen IT-Herausforderung? Wir sorgen für eine maßgeschneiderte Unterstützung. Informiere dich jetzt.

Hilf uns, noch besser zu werden.

Wir sind immer auf der Suche nach neuen Talenten. Auch für dich ist die passende Stelle dabei.