Reputation: 15435
I'm pretty new to using Akka actors. Very recently, I read about distributed actors or remote actors (whatever you call it). I have been comfortable so far using state in my Actor and mutate it inside my receive method. Happy days! Now I want to distribute my actor across several JVM's and I already see that my state in my actor could be an issue.
I did read about the become and unbecome and I was just wondering how Akka handles this internally?
With state:
class TestActor extends Actor {
var state = List.empty[String])
def receive =
case Add(elem) => state + elem
case Contains(elem) => sender() ! state.contains(elem)
}
}
With state removed:
class TestActor extends Actor {
def receive = start(List.empty[String])
def start(lst: List[String]): Receive = {
case Add(elem) =>
context become start(lst+ elem)
case Contains(elem) =>
sender() ! lst.contains(elem)
}
}
Where did the state go in the second version with the become example?
Upvotes: 1
Views: 2462
Reputation: 179199
The state is held by the Akka machinery. There's a stack of receive
"blocks"
that's mutated every time you call become
or unbecome
.
The stack is represented as an immutable List
bound to a var
.
There are two places where the List
is replaced:
Compared to Erlang, Akka couldn't use the execution stack created by a recursive call to encode state, because that means a blocking call. Akka cannot afford a blocking call on any of its threads, so it needs a var
.
Akka's receive
blocks may give the illusion of recursive calls, but remember, a receive
block returns a PartialFunction
which, when called, calls start
.
Upvotes: 3
Reputation: 751
Conceptually, the state is on the execution stack. Receive
is a type alias for a PartialFunction
. The partial function in the implementation of the start
method closes over the lst
parameter. The receive behavior can then be evaluated on demand using parameter values from the execution context.
So the size of lst
does indeed grow every time the start method is called.
In reference to leaking memory, note that by default become
is unnested so there is no memory leak. Only if you pass false
for the discardOld
parameter, allowing for a future unbecome
, can there be an issue with a memory leak.
Upvotes: 5
Reputation: 6925
When using become
/unbecome
you are just changing the behavior of your actor not the state. State remains the same. However, when using it you can be leaking memory (take a look to this example for more detail: https://github.com/akka/akka/blob/master/akka-docs/rst/scala/code/docs/actor/UnnestedReceives.scala)
One more thing worth mentioning is that if you actor gets restarted by the supervisor, it will start with its original behavior.
become/unbecome
is usually used either for initialization or a temporary change in state like for example flow control.
Upvotes: 1