The Akka documentation is documenting dangerous variants of using Props
:
// NOT RECOMMENDED within another actor:
// encourages to close over enclosing class
val props7 = Props(new MyActor)
Then carries on stating:
This method is not recommended to be used within another actor because
it encourages to close over the enclosing scope, resulting in
non-serializable Props and possibly race conditions (breaking the
actor encapsulation).
Could someone please explain the meaning of "closing over the enclosing scope"? Been looking all over and found nothing. Thanks.
It's a bit tricky to see in this example, that the new Actor
is passed in as a so called "by name" parameter. Think of it as if it is turned into a function of type () => Actor
. This function will be called every time the actor will be (re)created by it's supervisor during a restart.
The problem is that this function is a "closure" (very easy to Google ;)), which means it captures and remembers everything in the surrounding scope that it needs (sometimes, but very rarely referred to as "stack ripping"). E.g val f = (a: Int) => a + x
. Where does the x
come from? It comes from the surrounding scope. The function litetal, assigned to f
is called an "open term". At runtime the function literal becomes a function value (that's a fancy way of saying "object"), which when executed closes the open term, while capturing everything in the surrounding scope. That's where the name "closure" comes from.
Closures are very very useful, but you have to be careful what you close over. Sometimes x
is a def
or god forbid a var
, which leads to unpredictable results for f
, because you don't have control over the time when f
will be called/executed. Try it out!
Two very common anti paterns in Akka are/were:
- Closing over (the outer)
this
reference when creating an Actor
from an inner class.
- Closing over
def sender
when responding to
a message with a future.
I gave you a bunch of fancy terms to Google on purpose, btw ;)
Cheers and happy coding
As a supplement to @agilesteel's fine answer, some references:
Explains what closures are: Programming in Scala, 8.7, Closures
Explains why closures can cause serialization problems: SIP-21 - Spores
And here is a code example of creating a Props object that is not serializable because of closing over a non-serializable object, based on the example in SIP-21:
case class Helper(name: String)
object MyNonserializableObject {
val helper = Helper("the helper")
val props7 = Props(new MyActor(helper))
}
Even though helper
itself is serializable, the "new MyActor(helper)" is passed by name and so captures this.helper
, and this
is not serializable.
You can see that the actor parameter is passed by name from the signature of the Props
apply
method where there is a ⇒ in the creator
parameter:
def apply[T <: Actor](creator: ⇒ T)(implicit arg0: ClassTag[T]): Props