I am trying to implement an A/B Testing mechanism using Akka actors (scala 2.12.8, akka-actor-typed 2.6.1)
I designed it as:
- 1 A/B Testing root actor
- 2 Variant actors, children of the A/B Testing actor
When the A/B Testing actor will receive a message, it will choose one variant and forward the message to it.
However until now, I didn't managed to make the root actor spawn its children. Here is my code.
import akka.actor.typed.{ActorRef, ActorSystem, Behavior}
import akka.actor.typed.scaladsl.{AbstractBehavior, ActorContext, Behaviors}
import scala.util.Random
// My variants
object Ranker {
sealed trait Command
final case class Rank() extends Command
def apply(rank: Int): Behavior[Command] = Behaviors.setup { context => new Ranker(context, rank) }
}
class Ranker(context: ActorContext[Ranker.Command], rank: Int) extends AbstractBehavior[Ranker.Command](context) {
override def onMessage(msg: Ranker.Command): Behavior[Ranker.Command] = {
println(rank)
this
}
}
// The root A/B Testing actor
object ABTester {
def apply(): Behavior[Ranker.Command] = Behaviors.setup { context => new ABTester(context) }
}
class ABTester(context: ActorContext[Ranker.Command]) extends AbstractBehavior[Ranker.Command](context) {
val rng = new Random()
// Spawn children actors
val rankers: Seq[ActorRef[Ranker.Command]] = Seq(Ranker(1), Ranker(2)).zipWithIndex
.map { case (b, i) =>
println(s"Spawning ranker $i")
val actorRef = context.spawn(b, s"Ranker $i")
println(s"Spawning ranker $i: Done")
actorRef
}
override def onMessage(msg: Ranker.Command): Behavior[Ranker.Command] = {
rankers(rng.nextInt(rankers.size)) ! msg
this
}
}
// Application entry point
object Main {
def main(args: Array[String]): Unit = {
val actor = ActorSystem(ABTester(), "ABTester")
actor ! Ranker.Rank()
Thread.sleep(5000)
}
}
Running this sample will print:
Spawning ranker 0
but that's all, as if the line 31 (val actorRef = context.spawn(b, s"Ranker $i")) never returns...
Did I miss something about child actors spawning?