scalaakkaactor

How to keep reference to a created instance of Actor class with Akka?


I have a class that itself is an Actor:

class Client(server: Server, systemActor: ActorRef) extends Actor {
...
}

I have a list that manages connected clients. The actor that spawns the client actors watches a list off "servers" in the database, and spawns a client connection for each one like this:

private val clientList = mutable.ArrayBuffer.empty[Client]

class RegistrationWatcherActor(val systemActor: ActorSystem) extends Actor with Timers {

  implicit val system: ActorSystem = context.system

  timers.startSingleTimer(TickKey, FirstTick, FiniteDuration(1, TimeUnit.SECONDS)) // wait 10 seconds to give database time to initiate

  private def checkRegistrations(): Unit = {
    val database = DatabaseUtil.getInstance
    val serversOutgoing: Seq[Server] = database.getAll[Server](classOf[Server])

    for (server <- serversOutgoing) {
      val client = clientList.find{ client => client.server == server }
      if (client.isEmpty) { // client for this server was not found, so create one
        
      }
    }
  }

  def receive: Receive = {
    case FirstTick =>
      timers.startTimerWithFixedDelay(TickKey, Tick, FiniteDuration(1, TimeUnit.SECONDS))
    case Tick =>
      checkRegistrations()
  }
}

The Problem

In the check if (client.isEmpty) I would like to create an instance of Client, spawn it as an actor, and put the reference into the list so it doesn't get created again. As you can see, my method of identifying if the client has been created or not is my checking its serverID against the stored one in the database.

Ideally, I want to do:

val client = new Client(server, systemActor)
systemActor.actorOf(Props(client), server.serverID)
clientList.append(client)

Any suggestions please?

Alternative

I realised I can make clientList a list of String and store the serverID in here. However, I would eventually like the RegistrationWatcher to be able to have control over aspects of the client, so a reference to the Client object would be preferable.


Solution

  • As a general principle, part of the point of the actor model is that actors are autonomous. Wanting the RegistrationWatcherActor to "have control" over aspects of the Client's behavior/state is thus a sign that the decomposition of responsibilities for the actors is off.

    If there is some state that you truly need to share between actors (or between an actor and the outside world), note that the single-threaded illusion within the actors is being broken and you are basically back in the world of multithreaded concurrency (e.g. needing to use synchronized blocks, volatiles, concurrent collections, or atomics): even if you had a reference to the Client, there's no actual guarantee that calling methods on the Client from the RegistrationWatcherActor would be visible until the next time the RegistrationWatcherActor sent a message to the Client and that message was about to be processed. It's probably better in this situation to have the shared state be wrapped in an AtomicReference that's passed to the Client and save the AtomicReference in the RegistrationWatcherActor.