score:3

Accepted answer

if i understand the question correctly, then i can offer up a couple of ways you can accomplish this (though there are certainly others).

option 1

in this approach, there will be an actor that is responsible for waking up periodically and sending a request to all session actors to get their current stats. that actor will use actorselection with a wildcard to accomplish that goal. a rough outline if the code for this approach is as follows:

case class sessionstats(foo:int, bar:int)
case object getsessionstats

class sessionactor extends actor{
  def receive = {
    case getsessionstats =>
      println(s"${self.path} received a request to get stats")
      sender ! sessionstats(1, 2)
  }
}


case object gatherstats
class sessionstatsgatherer extends actor{
  context.system.scheduler.schedule(5 seconds, 5 seconds, self, gatherstats)(context.dispatcher)

  def receive = {
    case gatherstats =>
      println("waking up to gether stats")
      val sel = context.system.actorselection("/user/session*")
      sel ! getsessionstats

    case sessionstats(f, b) =>
      println(s"got session stats from ${sender.path}, values are $f and $b")
  }
}

then you could test this code with the following:

val system = actorsystem("test")
system.actorof(props[sessionactor], "session-1")
system.actorof(props[sessionactor], "session-2")

system.actorof(props[sessionstatsgatherer])

thread.sleep(10000)
system.actorof(props[sessionactor], "session-3")

so with this approach, as long as we use a naming convention, we can use an actor selection with a wildcard to always find all of the session actors even though they are constantly coming (starting) and going (stopping).

option 2

a somewhat similar approach, but in this one, we use a centralized actor to spawn the session actors and act as a supervisor to them. this central actor also contains the logic to periodically poll for stats, but since it's the parent, it does not need an actorselection and can instead just use its children list. that would look like this:

case object spawnsession
class sessionsmanager extends actor{
  context.system.scheduler.schedule(5 seconds, 5 seconds, self, gatherstats)(context.dispatcher)
  var sessioncount = 1

  def receive = {
    case spawnsession =>
      val session = context.actorof(props[sessionactor], s"session-$sessioncount")
      println(s"spawned session: ${session.path}")
      sessioncount += 1
      sender ! session

    case gatherstats =>
      println("waking up to get session stats")
      context.children foreach (_ ! getsessionstats)

    case sessionstats(f, b) =>
      println(s"got session stats from ${sender.path}, values are $f and $b")      
  }
}

and could be tested as follows:

val system = actorsystem("test")
val manager = system.actorof(props[sessionsmanager], "manager")
manager ! spawnsession
manager ! spawnsession
thread.sleep(10000)
manager ! spawnsession

now, these examples are extremely trivialized, but hopefully they paint a picture for how you could go about solving this issue with either actorselection or a management/supervision dynamic. and a bonus is that ask is not needed in either and also no blocking.

score:1

there have been many additional changes in this project, so my answer/comments have been delayed quite a bit :-/

first, the session stats gathering should not be periodical, but on request. my original idea was to "mis-use" the actor system as my map of all existing session actors, so that i would not need a supervisor actor knowing all sessions.

this goal has shown to be elusive - session actors depend on shared state, so the session creator must watch sessions anyways.

this makes option 2 the obvious answer here - the session creator has to watch all children anyways.

the most vexing hurdle with option 1 was "how to determine when all (current) answers are there" - i wanted the statistics request to take a snapshot of all currently existing actor names, query them, ignore failures (if a session dies before it can be queried, it can be ignored here) - the statistics request is only a debugging tool, i.e. something like a "best effort". the actor selection api tangled me up in a thicket of futures (i am a scala/akka newbie), so i gave up on this route.

option 2 is therefore better suited to my needs.


Related Query

More Query from same tag