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 variablecounter
.CreateChild
which creates a newChildActor
passing the value ofcounter
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.
More articles
fromBenedikt Ritter
Your job at codecentric?
Jobs
Agile Developer und Consultant (w/d/m)
Alle Standorte
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.
Blog author
Benedikt Ritter
Do you still have questions? Just send me a message.
Do you still have questions? Just send me a message.