How can I use a mutable Map in an Actor object to store values depending on the messages? My code
object TestActor {
final case class Command()
private val mmap: mutable.Map[Int,Map[Int,Int]] = mutable.Map[Int,Map[Int,Int]]()
def apply(): Behavior[Command] = Behaviors.receive {
case (context, Command()) =>
mmap = (1 -> (2 -> 3))
context.log.info(nmap)
}
}
This causes problems since the mmap
is static as it is a field in a scala object. So when multiple threads add some values to the map, it would result in strange values. Since I still need to use typed akka actors, is there a way to have a field in an object (or in some other way) to make it possible for me to still use objects to represent the actors using akka.typed
and still have fields that are not shared among the TestActor
actors?
CodePudding user response:
In Akka Typed's functional API (which this example uses), the typical way to do this would be along these lines:
object TestActor {
final case class Command()
def apply(): Behavior[Command] =
fromMap(mutable.Map.empty)
private def fromMap(mmap: mutable.Map[Int, Map[Int, Int]]): Behavior[Command] =
Behaviors.receive {
case (context, Command()) =>
mmap = (1 -> Map(2 -> 3))
context.log.info(mmap)
Behaviors.same
}
}
Here, the object
is basically serving as just a module and the fields of that object are better thought of as globals.
Note that there is also an object-oriented API for defining typed actors, which in Scala would be:
object TestActor {
final case class Command()
def apply(): Behavior[TestActor.Command] =
Behaviors.setup { context =>
new TestActor(context)
}
}
class TestActor(context: ActorContext[TestActor.Command]) extends AbstractBehavior[TestActor.Command](context) {
val mmap: mutable.Map[Int, Map[Int, Int]] = mutable.Map.empty
def onMessage(msg: TestActor.Command): Behavior[TestActor.Command] = {
mmap = (1 -> Map(2, 3))
context.log.info(mmap)
this
}
}
It's worth noting that in Akka, it's generally better to have var
s of immutable data (i.e. mutable containers holding immutable values) than val
s of mutable data (i.e. immutable containers holding mutable values), as that can expose a channel for something outside the actor (including another actor) to "pull the rug out from under" an actor.
In the functional API, this would typically take a form like:
object TestActor {
final case class Command()
def apply(): Behavior[Command] =
fromMap(Map.empty)
private def fromMap(map: Map[Int, Map[Int, Int]]): Behavior[Command] =
Behaviors.receive {
case (context, Command()) =>
val nextMap = map (1 -> Map(2 -> 3))
context.log.info(nextMap)
withMap(nextMap)
}
}
And in the OO Scala API:
// TestActor companion object is as before
class TestActor(context: ActorContext[TestActor.Command]) extends AbstractBehavior[TestActor.Command](context) {
var map: Map[Int, Map[Int, Int]] = Map.empty
def onMessage(msg: TestActor.Command): Behavior[TestActor.Command] = {
map = map (1 -> Map(2 -> 3))
context.log.info(map)
this
}
}