Reputation: 714
I'm trying to create a router that will deploy actors with a constructor arg that is a mutating method.
object LocalRouterArgEvalFrequency {
def main(args: Array[String]) {
val system = ActorSystem("TestSystem")
var number = 0
def getANumber: Int = {
number += 1
number
}
val router = system.actorOf(Props(classOf[Foo], getANumber)
.withRouter(RoundRobinRouter(5)), "RRRouter")
(1 to 10).foreach(_ => router ! "What's Your Number?")
}
}
class Foo(number: Int) extends Actor {
println("I got instantiated")
def receive: Actor.Receive = {
case msg: String => println(s"My Number is $number")
}
}
I would expect to see something along these lines as output (I think RoundRobin is best effort last I checked so no guarantees):
I got instantiated
I got instantiated
I got instantiated
I got instantiated
I got instantiated
My Number is 1
My Number is 2
My Number is 3
My Number is 4
My Number is 5
My Number is 1
My Number is 2
My Number is 3
My Number is 4
My Number is 5
Instead I see the following:
I got instantiated
I got instantiated
I got instantiated
I got instantiated
I got instantiated
My Number is 1
My Number is 1
My Number is 1
My Number is 1
My Number is 1
My Number is 1
My Number is 1
My Number is 1
My Number is 1
My Number is 1
I recognize that injecting a mutation like this is in all likely hood not a great idea pattern wise, but I have a use-case in test code that closely resembles this type of interaction.
Is there a way to get the Props to evaluate every time with the router deploy? Or perhaps there's a different way to go about this that I'm not seeing...Thanks in advance!
Upvotes: 0
Views: 659
Reputation: 35463
One thing to keep in mind is that the constructor arg value is captured one time with your current approach. Put another way, getANumber
is really only called one time and the result of that (1 in this case) is used as the constructor arg to all actors created by the router. If you want each to have a unique number then you can try something like this instead:
object LocalRouterArgEvalFrequency {
def main(args: Array[String]) {
val system = ActorSystem("TestSystem")
var number = 0
def getANumber: Int = {
number += 1
number
}
val routees = List.fill(5)(getANumber).map(i => system.actorOf(Props(classOf[Foo], i)))
val router = system.actorOf(Props.empty.withRouter(RoundRobinRouter(routees = routees)), "RRRouter")
(1 to 10).foreach(_ => router ! "What's Your Number?")
}
}
class Foo(number: Int) extends Actor {
println("I got instantiated: " + number)
def receive: Actor.Receive = {
case msg: String => println(s"My Number is $number")
}
}
EDIT
You could also create your router using a different variant of Props.apply
like so:
val router = system.actorOf(Props(new Foo(getANumber)).withRouter(RoundRobinRouter(5)), "RRRouter")
That approach might be more of what you were looking for.
Upvotes: 3