Class ConsistentHash

java.lang.Object
io.axoniq.framework.springcloud.routing.ConsistentHash

public final class ConsistentHash extends Object
An immutable consistent-hash ring resolving a routing key to the Member that should handle a command.

Each member claims a number of positions on the ring proportional to its load factor. Resolving a routing key hashes it onto the ring and walks clockwise until a member is found that handles the command in question. The same routing key therefore always resolves to the same member for as long as the memberships do not change, which is what keeps commands for one entity on one node.

Instances are immutable: with(Member, MemberCapabilities) and without(Member) return a new ring rather than mutating this one, so a ring can be published to readers without further synchronisation. Every change increases the version().

A member with a load factor of 0 — including one whose capabilities could not be retrieved, reported as MemberCapabilities.INCAPABLE — claims no positions and is never resolved to, but remains part of members().

Since:
5.4.0
Author:
Allard Buijze
  • Constructor Details

    • ConsistentHash

      public ConsistentHash()
      Constructs an empty ConsistentHash hashing routing keys and ring positions with an MD5 digest.

      Register members through with(Member, MemberCapabilities).

    • ConsistentHash

      public ConsistentHash(UnaryOperator<String> hashFunction)
      Constructs an empty ConsistentHash using the given hashFunction to hash both routing keys and the ring positions claimed by each member.

      Every member of the cluster must use the same hash function, or they will disagree on where commands belong. Register members through with(Member, MemberCapabilities).

      Parameters:
      hashFunction - the function hashing a routing key, or a member's ring position, onto the ring. Must be deterministic, and must order its output consistently for all members of the cluster.
  • Method Details

    • withOnly

      public ConsistentHash withOnly(Map<Member,MemberCapabilities> memberships)
      Returns a ConsistentHash holding exactly the given memberships, hashed with this ring's hash function.

      Intended for rebuilding a ring from scratch, as a discovery round does. Registering members one by one through with(Member, MemberCapabilities) would recompute every member's ring positions on each call, making a rebuild quadratic in the number of members; this assigns the positions once.

      A member this ring already holds under the same capabilities keeps the positions it has, rather than having them computed again, which makes rebuilding a ring in which little changed cheap.

      Members this ring holds that are absent from memberships are not carried over — the result describes the cluster as memberships describes it, which is what makes it a rebuild rather than an update.

      Parameters:
      memberships - the members to register, and the messages each of them handles
      Returns:
      a ConsistentHash holding exactly the given memberships, or this when it already holds exactly those memberships
    • with

      public ConsistentHash with(Member member, MemberCapabilities capabilities)
      Returns a ConsistentHash with the given member registered under the given capabilities, replacing any earlier registration of a member with the same name.
      Parameters:
      member - the member to register
      capabilities - the messages the given member handles, and the command load it asks for
      Returns:
      a ConsistentHash including the given member, or this when the given member was already registered with identical capabilities
    • without

      public ConsistentHash without(Member member)
      Returns a ConsistentHash without the given member.
      Parameters:
      member - the member to deregister
      Returns:
      a ConsistentHash excluding the given member, or this when the given member was not registered
    • memberFor

      public Optional<Member> memberFor(String routingKey, QualifiedName commandName)
      Resolves the Member that should handle a command of the given commandName carrying the given routingKey.
      Parameters:
      routingKey - the routing key of the command to resolve a member for
      commandName - the QualifiedName of the command to resolve a member for. Only members that subscribed to this name are considered.
      Returns:
      the member that should handle the command, or Optional.empty() when no registered member handles commands of the given commandName
    • queryHandlers

      public List<Member> queryHandlers(QualifiedName queryName)
      Returns every Member registered with this ring that handles queries of the given queryName, ordered by name.

      The order is stable for as long as the memberships do not change, so that a caller rotating over the result rotates over the same sequence on every call. The result is computed once per query name and ring.

      Parameters:
      queryName - the QualifiedName of the query to resolve the handling members of
      Returns:
      every registered member handling the given queryName, empty when none does
    • members

      public Set<Member> members()
      Returns every Member registered with this ring, including those claiming no ring positions.
      Returns:
      every registered member, in no particular order
    • capabilitiesOf

      public Optional<MemberCapabilities> capabilitiesOf(Member member)
      Returns the MemberCapabilities the given member is registered under.
      Parameters:
      member - the member to look up the capabilities of
      Returns:
      the capabilities of the given member, or Optional.empty() when it is not registered
    • version

      public int version()
      Returns the version of this ring, increased by one on every change in memberships.

      Two rings with the same version are not necessarily equal — the version counts changes made along one lineage of rings, and says nothing about rings built independently.

      Returns:
      the number of changes made to reach this ring
    • equals

      public boolean equals(Object o)
      Overrides:
      equals in class Object
    • hashCode

      public int hashCode()
      Overrides:
      hashCode in class Object
    • toString

      public String toString()
      Overrides:
      toString in class Object