diff --git a/akka-cluster/src/main/resources/reference.conf b/akka-cluster/src/main/resources/reference.conf index d53aece519..8e69ea6d20 100644 --- a/akka-cluster/src/main/resources/reference.conf +++ b/akka-cluster/src/main/resources/reference.conf @@ -65,6 +65,11 @@ akka { # move 'WeaklyUp' members to 'Up' status once convergence has been reached. allow-weakly-up-members = on + # Teams are used to make islands of the cluster that are colocated. It can be used + # to make the cluster "dc-aware", run the cluster in multiple availability zones or regions. + # The team is added to the list of roles of the node with the prefix "team-". + team = "default" + # The roles of this member. List of strings, e.g. roles = ["A", "B"]. # The roles are part of the membership information and can be used by # routers or other services to distribute work to certain member types, diff --git a/akka-cluster/src/main/scala/akka/cluster/ClusterSettings.scala b/akka-cluster/src/main/scala/akka/cluster/ClusterSettings.scala index 80453ce1d8..44b92a8009 100644 --- a/akka-cluster/src/main/scala/akka/cluster/ClusterSettings.scala +++ b/akka-cluster/src/main/scala/akka/cluster/ClusterSettings.scala @@ -93,7 +93,8 @@ final class ClusterSettings(val config: Config, val systemName: String) { val AllowWeaklyUpMembers = cc.getBoolean("allow-weakly-up-members") - val Roles: Set[String] = immutableSeq(cc.getStringList("roles")).toSet + val Team: String = cc.getString("team") + val Roles: Set[String] = immutableSeq(cc.getStringList("roles")).toSet + s"team-$Team" val MinNrOfMembers: Int = { cc.getInt("min-nr-of-members") } requiring (_ > 0, "min-nr-of-members must be > 0") diff --git a/akka-cluster/src/main/scala/akka/cluster/Member.scala b/akka-cluster/src/main/scala/akka/cluster/Member.scala index 2fcb9bdf35..145d14f9ef 100644 --- a/akka-cluster/src/main/scala/akka/cluster/Member.scala +++ b/akka-cluster/src/main/scala/akka/cluster/Member.scala @@ -22,6 +22,9 @@ class Member private[cluster] ( val status: MemberStatus, val roles: Set[String]) extends Serializable { + lazy val team = roles.find(_.startsWith("team-")) + .getOrElse(throw new IllegalStateException("Team undefined, should not be possible")) + def address: Address = uniqueAddress.address override def hashCode = uniqueAddress.## diff --git a/akka-cluster/src/multi-jvm/scala/akka/cluster/MBeanSpec.scala b/akka-cluster/src/multi-jvm/scala/akka/cluster/MBeanSpec.scala index 8ac5e092b7..37ed3d24ae 100644 --- a/akka-cluster/src/multi-jvm/scala/akka/cluster/MBeanSpec.scala +++ b/akka-cluster/src/multi-jvm/scala/akka/cluster/MBeanSpec.scala @@ -120,28 +120,32 @@ abstract class MBeanSpec | { | "address": "${sortedNodes(0)}", | "roles": [ - | "testNode" + | "testNode", + | "team-default" | ], | "status": "Up" | }, | { | "address": "${sortedNodes(1)}", | "roles": [ - | "testNode" + | "testNode", + | "team-default" | ], | "status": "Up" | }, | { | "address": "${sortedNodes(2)}", | "roles": [ - | "testNode" + | "testNode", + | "team-default" | ], | "status": "Up" | }, | { | "address": "${sortedNodes(3)}", | "roles": [ - | "testNode" + | "testNode", + | "team-default" | ], | "status": "Up" | } diff --git a/akka-cluster/src/multi-jvm/scala/akka/cluster/QuickRestartSpec.scala b/akka-cluster/src/multi-jvm/scala/akka/cluster/QuickRestartSpec.scala index 3e1da843f2..df14986ac1 100644 --- a/akka-cluster/src/multi-jvm/scala/akka/cluster/QuickRestartSpec.scala +++ b/akka-cluster/src/multi-jvm/scala/akka/cluster/QuickRestartSpec.scala @@ -91,7 +91,7 @@ abstract class QuickRestartSpec Cluster(system).state.members.size should ===(totalNumberOfNodes) Cluster(system).state.members.map(_.status == MemberStatus.Up) // use the role to test that it is the new incarnation that joined, sneaky - Cluster(system).state.members.flatMap(_.roles) should ===(Set(s"round-$n")) + Cluster(system).state.members.flatMap(_.roles) should ===(Set(s"round-$n", "team-default")) } } enterBarrier("members-up-" + n) diff --git a/akka-cluster/src/test/scala/akka/cluster/ClusterConfigSpec.scala b/akka-cluster/src/test/scala/akka/cluster/ClusterConfigSpec.scala index 481556bd77..171e67d42c 100644 --- a/akka-cluster/src/test/scala/akka/cluster/ClusterConfigSpec.scala +++ b/akka-cluster/src/test/scala/akka/cluster/ClusterConfigSpec.scala @@ -5,9 +5,13 @@ package akka.cluster import language.postfixOps +import scala.concurrent.duration._ + +import com.typesafe.config.ConfigFactory + import akka.testkit.AkkaSpec import akka.dispatch.Dispatchers -import scala.concurrent.duration._ + import akka.remote.PhiAccrualFailureDetector import akka.util.Helpers.ConfigOps import akka.actor.Address @@ -41,7 +45,8 @@ class ClusterConfigSpec extends AkkaSpec { DownRemovalMargin should ===(Duration.Zero) MinNrOfMembers should ===(1) MinNrOfMembersOfRole should ===(Map.empty[String, Int]) - Roles should ===(Set.empty[String]) + Team should ===("default") + Roles should ===(Set("team-default")) JmxEnabled should ===(true) UseDispatcher should ===(Dispatchers.DefaultDispatcherId) GossipDifferentViewProbability should ===(0.8 +- 0.0001) @@ -49,5 +54,20 @@ class ClusterConfigSpec extends AkkaSpec { SchedulerTickDuration should ===(33 millis) SchedulerTicksPerWheel should ===(512) } + + "be able to parse non-default cluster config elements" in { + val settings = new ClusterSettings(ConfigFactory.parseString( + """ + |akka { + | cluster { + | roles = [ "hamlet" ] + | team = "blue" + | } + |} + """.stripMargin).withFallback(ConfigFactory.load()), system.name) + import settings._ + Roles should ===(Set("hamlet", "team-blue")) + Team should ===("blue") + } } } diff --git a/akka-docs/src/main/paradox/java/cluster-team.md b/akka-docs/src/main/paradox/java/cluster-team.md new file mode 120000 index 0000000000..b25b4db61f --- /dev/null +++ b/akka-docs/src/main/paradox/java/cluster-team.md @@ -0,0 +1 @@ +../scala/cluster-team.md \ No newline at end of file diff --git a/akka-docs/src/main/paradox/scala/cluster-team.md b/akka-docs/src/main/paradox/scala/cluster-team.md new file mode 100644 index 0000000000..436d5a9ad8 --- /dev/null +++ b/akka-docs/src/main/paradox/scala/cluster-team.md @@ -0,0 +1,15 @@ +# Cluster Team + +@@@ note + +Cluster teams are a work-in-progress feature, and behavior is still expected to change. + +@@@ + +Teams are used to define islands of the cluster that are colocated. +They can be used to make the cluster "dc-aware", run the cluster in multiple availability zones or regions. + +Cluster nodes can be assigned to a team by setting the `akka.cluster.team` setting. +When no team is specified, a node will belong to the 'default' team. + +The team is added to the list of roles of the node with the prefix "team-". \ No newline at end of file