package org.infinispan.distribution.ch; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.function.UnaryOperator; import org.infinispan.commons.hash.Hash; import org.infinispan.commons.util.SmallIntSet; import org.infinispan.distribution.LocalizedCacheTopology; import org.infinispan.globalstate.ScopedPersistentState; import org.infinispan.remoting.transport.Address; /** * A consistent hash algorithm implementation. Implementations would typically be constructed via a * {@link ConsistentHashFactory}. * * A consistent hash assigns each key a list of owners; the number of owners is defined at creation time, * but the consistent hash is free to return a smaller or a larger number of owners, depending on * circumstances, as long as each key has at least one owner. * * The first element in the list of owners is the "primary owner". A key will always have a primary owner. * The other owners are called "backup owners". * * This interface gives access to some implementation details of the consistent hash. * * Our consistent hashes work by splitting the hash space (the set of possible hash codes) into * fixed segments and then assigning those segments to nodes dynamically. The number of segments * is defined at creation time, and the mapping of keys to segments never changes. * The mapping of segments to nodes can change as the membership of the cache changes. * * Normally application code doesn't need to know about this implementation detail, but some * applications may benefit from the knowledge that all the keys that map to one segment are * always located on the same server. * * @see <a href="https://community.jboss.org/wiki/Non-BlockingStateTransferV2">Non-BlockingStateTransferV2</a> * * @author Manik Surtani * @author Mircea.Markus@jboss.com * @author Dan Berindei * @author anistor@redhat.com * @since 4.0 */ public interface ConsistentHash { /** * @return The configured number of owners for each key. Note that {code @getOwners(key)} may return * a different number of owners. */ int getNumOwners(); /** * @deprecated Since 8.2, the {@code Hash} is optional - replaced in the configuration by the * {@code KeyPartitioner} */ @Deprecated default Hash getHashFunction() { throw new UnsupportedOperationException(); } /** * @return The actual number of hash space segments. Note that it may not be the same as the number * of segments passed in at creation time. */ int getNumSegments(); /** * Should return the addresses of the nodes used to create this consistent hash. * * @return set of node addresses. */ List<Address> getMembers(); /** * Should be equivalent to return the first element of {@link #locateOwners}. * Useful as a performance optimization, as this is a frequently needed information. * @param key key to locate * @return the address of the owner * @deprecated Since 9.0, please use {@link LocalizedCacheTopology#getDistribution(Object)} instead. */ @Deprecated default Address locatePrimaryOwner(Object key) { return locatePrimaryOwnerForSegment(getSegment(key)); } /** * Finds all the owners of a key. The first element in the returned list is the primary owner. * * @param key key to locate * @return An unmodifiable list of addresses where the key resides. * Will never be {@code null}, and it will always have at least 1 element. * @deprecated Since 9.0, please use {@link LocalizedCacheTopology#getDistribution(Object)} instead. */ @Deprecated default List<Address> locateOwners(Object key) { return locateOwnersForSegment(getSegment(key)); } /** * @deprecated Since 9.0, please use {@link LocalizedCacheTopology#getWriteOwners(Collection)} instead. */ @Deprecated default Set<Address> locateAllOwners(Collection<Object> keys) { // Use a HashSet assuming most of the time the number of keys is small. HashSet<Address> owners = new HashSet<>(); SmallIntSet segments = new SmallIntSet(getNumSegments()); for (Object key : keys) { int segment = getSegment(key); if (segments.add(segment)) { owners.addAll(locateOwnersForSegment(segment)); } if (owners.size() == getMembers().size()) { return owners; } } return owners; } /** * Test to see whether a key is owned by a given node. * * @param nodeAddress address of the node to test * @param key key to test * @return {@code true} if the key is mapped to the address; {@code false} otherwise * @deprecated Since 9.0, please use {@link LocalizedCacheTopology#isReadOwner(Object)} and {@link LocalizedCacheTopology#isWriteOwner(Object)} instead. */ @Deprecated default boolean isKeyLocalToNode(Address nodeAddress, Object key) { return locateOwnersForSegment(getSegment(key)).contains(nodeAddress); } /** * @return The hash space segment that a key maps to. * * @deprecated Since 9.0, please use {@link KeyPartitioner#getSegment(Object)} * or {@link LocalizedCacheTopology#getSegment(Object)} instead. */ @Deprecated int getSegment(Object key); /** * @return All the nodes that own a given hash space segment, first address is the primary owner. The returned list is unmodifiable. */ List<Address> locateOwnersForSegment(int segmentId); /** * @return The primary owner of a given hash space segment. This is equivalent to {@code locateOwnersForSegment(segmentId).get(0)} but is more efficient */ Address locatePrimaryOwnerForSegment(int segmentId); /** * Check if a segment is local to a given member. * * <p>Implementation note: normally key-based method are implemented based on segment-based methods. * Here, however, we need a default implementation for the segment-based method for * backwards-compatibility reasons.</p> * * @since 8.2 */ default boolean isSegmentLocalToNode(Address nodeAddress, int segmentId) { return locateOwnersForSegment(segmentId).contains(nodeAddress); } /** * @return {@code true} if every member owns every segment. This allows callers to skip computing the * segment of a key in some cases. */ default boolean isReplicated() { // Returning true is only an optimization, so it's ok to return false by default. return false; } /** * Returns the segments owned by a cache member. * * @param owner the address of the member * @return a non-null set of segment IDs, may or may not be unmodifiable, which shouldn't be modified by caller */ Set<Integer> getSegmentsForOwner(Address owner); /** * Returns the segments that this cache member is the primary owner for. * @param owner the address of the member * @return a non-null set of segment IDs, may or may not be unmodifiable, which shouldn't be modified by caller */ Set<Integer> getPrimarySegmentsForOwner(Address owner); /** * Returns a string containing all the segments and their associated addresses. */ String getRoutingTableAsString(); /** * Writes this ConsistentHash to the specified scoped state. Before invoking this method, the ConsistentHash * addresses will have to be replaced with their corresponding {@link org.infinispan.topology.PersistentUUID}s * * @param state the state to which this ConsistentHash will be written */ default void toScopedState(ScopedPersistentState state) { throw new UnsupportedOperationException(); } /** * Returns a new ConsistentHash with the addresses remapped according to the provided {@link UnaryOperator}. * If an address cannot me remapped (i.e. the remapper returns null) this method should return null. * * @param remapper the remapper which given an address replaces it with another one * @return the remapped ConsistentHash or null if one of the remapped addresses is null */ default ConsistentHash remapAddresses(UnaryOperator<Address> remapper) { throw new UnsupportedOperationException(); } }