Search code examples
scalaakkaakka-clusterakka-actor

AKKA- how to block the creation of an actor if its name is not uniqe in the cluster


I'm trying to block the possibility of having actors in the system that are sharing the same name(they are on different paths so InvalidActorNameException will not be thrown)

application.conf:

someactor {
  akka.remote.netty.tcp.port = 6405
  akka.cluster.auto-down-unreachable-after = 20s
  akka.cluster.seed-nodes = ["akka.tcp://[email protected]:2552"]
  akka.actor.provider = "akka.cluster.ClusterActorRefProvider"
}

Main:

object SomeActor extends App {
  val system  =  ActorSystem("mySys", ConfigFactory.load("application").getConfig("someactor"))
  val t = system.actorOf(Props(classOf[SomeActor]), "someActor")
}

Actor:

class SomeActor extends Actor {
    val cluster = Cluster(SomeActor.system)
    override def receive = {
       case x=> println(x)
    }
}

If you run the application once with the 6405 port and once with 6406 port then the application will work, but I want it to notice that the system already contains an actor with the name "someActor" and block that call.

I don't mind adding the name as the role or to other config if it will be able to block by that but I can't have a state like a map containing the already existing names(or an actor containing the map with message passing) or to have a long running operation like actorSelection(and in any case they won't be safe if actorOf is called from multiple places in parallel).


Solution

  • I managed to do it with Cluster Aware Router of type group(each actor will run on remote node). The role of the node is the name "someActor" , I'm initializing the actor on the remote node with the same name "someActor"(so I'll know what the path to this actor) and router totalInstances config equal to 1(so only one node will be part of the router)

    Router init:

    context.actorOf(
            ClusterRouterGroup(RoundRobinGroup(Nil), ClusterRouterGroupSettings(
              totalInstances = 1, routeesPaths = List("/user/someActor"),
              allowLocalRoutees = false, useRole = Some("someActor"))).props()
    

    The remote actor:

    object RemoteActor extends App{
      val system = ActorSystem("mySys",ConfigFactory.load("remoteActorConfig"))
      system.actorOf(Props[RemoteActor], "someActor")
    
    }
    
    class RemoteActor extends Actor with ActorLogging{
      override def receive: Receive = {
        case x =>
          log.info(s"got: $x}")
      }
    }
    

    and the remoteActorConfig :

    akka{
      remote.netty.tcp.port = 0
      cluster.auto-down-unreachable-after = 20s
      cluster.seed-nodes = ["akka.tcp://[email protected]:2552"]
      cluster.roles.1 = "someActor"
      actor.provider = "akka.cluster.ClusterActorRefProvider"
    }
    

    Now if I'll run twice RemoteActor, run the app that initialize the router and send a broadcast message to the router- only one actor RemoterActor will receive it (and always the same one).