How to properly initialize a cluster node role name

I am using Akka 2.5 with Java 9. I am testing different ways to start up a cluster. I am not using routing. I would like to know if there a simple way to initialize a cluster node’s role name with the same string that was used to create the corresponding ActorRef in the node’s actor system.

Let’s say I have a non-actor java application that spins up a cluster of two actor system nodes with roles named “A” and “B” as defined in application.conf:

public class AkkaTestCluster
{
        String hcStr = "akka.remote.netty.tcp.port=6111" + "\n" + "akka.remote.artery.canonical.port=6111";
        Config config = ConfigFactory.parseString(hcStr).withFallback(ConfigFactory.load());
        ActorSystem actorSystemA = ActorSystem.create(CLUSTER_SYSTEM_NAME, config);
    	ActorRef actorRefA = actorSystemA.actorOf(Props.create(AkkaTestClusterListener.class), "A");

        hcStr = "akka.remote.netty.tcp.port=6112" + "\n" + "akka.remote.artery.canonical.port=6112";
        config = ConfigFactory.parseString(hcStr).withFallback(ConfigFactory.load());
        ActorSystem actorSystemB = ActorSystem.create(CLUSTER_SYSTEM_NAME, config);
    	ActorRef actorRefB = actorSystemB.actorOf(Props.create(AkkaTestClusterListener.class), "B");
}

In each case, an actor listens to the node that came up:`

public class AkkaTestClusterListener extends AbstractActor
{
    	private String nodeRole;

    	...

	/** Subscribe to cluster events. */
        @Override
        public Receive createReceive()
        {
            return receiveBuilder().match(MemberUp.class, this::processMemberUpEevent).build();
    	}


    	private void processMemberUpEevent(MemberUp mUp)
    	{
            Member member = mUp.member();
            Option scOpt = member.address().port();

            // convert from scala.Option<Object> type to java.util.Optional<Integer>
            Optional<Integer> optional = Optional.ofNullable((Integer) scOpt.getOrElse(null));
            assert optional != null;

            // the following works ok but it smells-
	    // obtain the role of the node using the member's detected port number as defined in application.conf
            int port = optional.get().intValue();
            switch (port)
            {
                case 6111:
                    this.nodeRole = "A";
                    break;
                case 6112:
                    this.nodeRole = "B";
                    break;
                default:
                    this.nodeRole = "NOT_SET";
                    break;
            }
}

The application.conf cluster section contains

cluster
	{
    	seed-nodes = ["akka://MyTestClusterSystem@myhost:6111",
            	      "akka://MyTestClusterSystem@myhost:6112"]
    	roles = ["seed", "A", "B"]
    	role { seed.min-nr-of-members = 2 }
	}

Could someone please tell me if there’s a better or more elegant way to initialize String nodeRole?

I’m not quite sure I understand the question, but, you assign the node roles in the config during start up, like you show in application.conf, and then you can access it through the cluster extension, like so:

Set<String> roles = Cluster.get(actorSystem).getSelfRoles();

Was this the question, or is it how to dynamically construct the config you start the actor system with?

Sorry I didn’t make my question more clear, or perhaps I have a fundamental misconception that needs to be cleared.

I want to start a cluster consisting of two nodes with different roles “A” and “B” defined in the roles array of application.conf as shown above.

I start one cluster node like this:

    ActorSystem actorSystemA = ActorSystem.create(CLUSTER_SYSTEM_NAME, config);
	ActorRef actorRefA = actorSystemA.actorOf(Props.create(AkkaTestClusterListener.class), "A");

then I start another node like this:

    ActorSystem actorSystemB = ActorSystem.create(CLUSTER_SYSTEM_NAME, config);
	ActorRef actorRefB = actorSystemB.actorOf(Props.create(AkkaTestClusterListener.class), "B");

But when each node comes up and processMemberUpEevent(MemberUp mUp) is called by AkkaTestClusterListener.createReceive() I have no way of knowing if it was called for node “A” or for node “B”.

Sure I can access the roles Set as you suggest, but that just gives me a set of all the roles. It doesn’t tell me which actor system (actorSystemA or actorSystemB) was started, even though I named each top ActorRef “A” or “B” when they were created.

Cluster node roles are a set to allow nodes to have many roles. In your configuration all nodes will have both roles A and B. If you want just node 1 to be in role A and just node 2 to be in role B you must start the node with the corresponding configuration.

Usually that would be for deployment so it’d be different config files, or passed in by setting system properties or environment variables when starting the JVM with the node. You can do it programatically as well, like so:

// load application.conf
final Config applicationConf = ConfigFactory.load();

// override some config programmtically
final Map<String, Object> properties = new HashMap<>();
properties.put("akka.cluster.roles", Arrays.asList("A", "seed"));
final Config programmaticOverrides = ConfigFactory.parseMap(properties);

final Config config = programmaticOverrides.withFallback(applicationConf);
final ActorSystem system = ActorSystem.create("my-system", config);


final Cluster cluster = Cluster.get(system);
cluster.join(cluster.selfAddress());

system.log().info("I got roles: {}", cluster.getSelfRoles());

Thanks for the explanation.
Should it still possible in this situation to require both nodes “A” and “B” to be up by setting property
akka.cluster.role { seed.min-nr-of-members = 2 }
for the cluster to be up? (doesn’t seem to work for me).

Yes that should work.

As long as you also have the role seed as well as your roles A and B, that will make the cluster not see the nodes as up until there are at least two of them with that role. This can easiest be observed by either enabling debug logging for Akka and look at the logs, or subscribe to the MemberUp cluster event.

Great. Thanks very much for your help.