org.infinispan.distribution.ch.impl.DefaultConsistentHash Maven / Gradle / Ivy
package org.infinispan.distribution.ch.impl;
import net.jcip.annotations.Immutable;
import org.infinispan.commons.hash.Hash;
import org.infinispan.commons.marshall.InstanceReusingAdvancedExternalizer;
import org.infinispan.commons.util.Immutables;
import org.infinispan.commons.util.Util;
import org.infinispan.distribution.ch.ConsistentHash;
import org.infinispan.globalstate.ScopedPersistentState;
import org.infinispan.marshall.core.Ids;
import org.infinispan.remoting.transport.Address;
import org.infinispan.remoting.transport.jgroups.JGroupsAddressCache;
import org.infinispan.topology.PersistentUUID;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Default {@link ConsistentHash} implementation. This object is immutable.
*
* @author Dan Berindei
* @author [email protected]
* @since 5.2
*/
@Immutable
public class DefaultConsistentHash implements ConsistentHash {
// State constants
private static final String STATE_CAPACITY_FACTOR = "capacityFactor.%d";
private static final String STATE_CAPACITY_FACTORS = "capacityFactors";
private static final String STATE_NUM_OWNERS = "numOwners";
private static final String STATE_NUM_SEGMENTS = "numSegments";
private static final String STATE_SEGMENT_OWNER = "segmentOwner.%d.%d";
private static final String STATE_SEGMENT_OWNERS = "segmentOwners";
private static final String STATE_SEGMENT_OWNER_COUNT = "segmentOwner.%d.num";
private final Hash hashFunction;
private final int numOwners;
/**
* The membership of the cache topology that uses this CH.
*/
private final List members;
private final float[] capacityFactors;
/**
* The routing table.
*/
private final List[] segmentOwners;
private final int segmentSize;
public DefaultConsistentHash(Hash hashFunction, int numOwners, int numSegments, List members,
Map capacityFactors, List[] segmentOwners) {
if (numSegments < 1)
throw new IllegalArgumentException("The number of segments must be strictly positive");
if (numOwners < 1)
throw new IllegalArgumentException("The number of owners must be strictly positive");
this.numOwners = numOwners;
this.hashFunction = hashFunction;
this.members = new ArrayList(members);
if (capacityFactors == null) {
this.capacityFactors = null;
} else {
this.capacityFactors = new float[members.size()];
for (int i = 0; i < this.capacityFactors.length; i++) {
this.capacityFactors[i] = capacityFactors.get(members.get(i));
}
}
this.segmentOwners = new List[numSegments];
for (int i = 0; i < numSegments; i++) {
if (segmentOwners[i] == null || segmentOwners[i].isEmpty()) {
throw new IllegalArgumentException("Segment owner list cannot be null or empty");
}
this.segmentOwners[i] = Immutables.immutableListCopy(segmentOwners[i]);
}
this.segmentSize = Util.getSegmentSize(numSegments);
}
// Only used by the externalizer, so we can skip copying collections
private DefaultConsistentHash(Hash hashFunction, int numOwners, int numSegments, List members,
float[] capacityFactors, List[] segmentOwners) {
if (numSegments < 1)
throw new IllegalArgumentException("The number of segments must be strictly positive");
if (numOwners < 1)
throw new IllegalArgumentException("The number of owners must be strictly positive");
this.numOwners = numOwners;
this.hashFunction = hashFunction;
this.members = members;
this.capacityFactors = capacityFactors;
for (int i = 0; i < numSegments; i++) {
if (segmentOwners[i] == null || segmentOwners[i].size() == 0) {
throw new IllegalArgumentException("Segment owner list cannot be null or empty");
}
}
this.segmentOwners = segmentOwners;
this.segmentSize = Util.getSegmentSize(numSegments);
}
DefaultConsistentHash(ScopedPersistentState state) {
this.numOwners = Integer.parseInt(state.getProperty(STATE_NUM_OWNERS));
int numMembers = Integer.parseInt(state.getProperty(ConsistentHashPersistenceConstants.STATE_MEMBERS));
this.members = new ArrayList<>(numMembers);
for(int i = 0; i < numMembers; i++) {
PersistentUUID uuid = PersistentUUID.fromString(state.getProperty(String.format(ConsistentHashPersistenceConstants.STATE_MEMBER, i)));
Address address = JGroupsAddressCache.fromPersistentUUID(uuid);
if (address == null) {
throw new IllegalStateException("Unknown address for "+uuid);
}
this.members.add(address);
}
int numSegments = state.getIntProperty(STATE_NUM_SEGMENTS);
this.segmentSize = Util.getSegmentSize(numSegments);
this.segmentOwners = new List[numSegments];
for (int i = 0; i < segmentOwners.length; i++) {
int segmentOwnerCount = Integer.parseInt(state.getProperty(String.format(STATE_SEGMENT_OWNER_COUNT, i)));
segmentOwners[i] = new ArrayList<>();
for (int j = 0; j < segmentOwnerCount; j++) {
PersistentUUID uuid = PersistentUUID.fromString(state.getProperty(String.format(STATE_SEGMENT_OWNER, i, j)));
segmentOwners[i].add(JGroupsAddressCache.fromPersistentUUID(uuid));
}
}
int numCapacityFactors = Integer.parseInt(state.getProperty(STATE_CAPACITY_FACTORS));
this.capacityFactors = new float[numCapacityFactors];
for (int i = 0; i < numCapacityFactors; i++) {
this.capacityFactors[i] = Float.parseFloat(state.getProperty(String.format(STATE_CAPACITY_FACTOR, i)));
}
this.hashFunction = Util.getInstance(state.getProperty(ConsistentHashPersistenceConstants.STATE_HASH_FUNCTION), null);
}
@Override
public Hash getHashFunction() {
return hashFunction;
}
@Override
public int getNumSegments() {
return segmentOwners.length;
}
@Override
public Set getSegmentsForOwner(Address owner) {
if (owner == null) {
throw new IllegalArgumentException("owner cannot be null");
}
if (!members.contains(owner)) {
throw new IllegalArgumentException("Node " + owner + " is not a member");
}
Set segments = new HashSet();
for (int segment = 0; segment < segmentOwners.length; segment++) {
if (segmentOwners[segment].contains(owner)) {
segments.add(segment);
}
}
return segments;
}
@Override
public Set getPrimarySegmentsForOwner(Address owner) {
if (owner == null) {
throw new IllegalArgumentException("owner cannot be null");
}
if (!members.contains(owner)) {
throw new IllegalArgumentException("Node " + owner + " is not a member");
}
Set segments = new HashSet();
for (int segment = 0; segment < segmentOwners.length; segment++) {
if (owner.equals(segmentOwners[segment].get(0))) {
segments.add(segment);
}
}
return segments;
}
@Override
public int getSegment(Object key) {
// The result must always be positive, so we make sure the dividend is positive first
return getNormalizedHash(key) / segmentSize;
}
public int getNormalizedHash(Object key) {
return hashFunction.hash(key) & Integer.MAX_VALUE;
}
/**
* @deprecated Since 8.2, use {@link HashFunctionPartitioner#getSegmentEndHashes()} instead.
*/
@Deprecated
public List getSegmentEndHashes() {
int numSegments = segmentOwners.length;
List hashes = new ArrayList(numSegments);
for (int i = 0; i < numSegments; i++) {
hashes.add(((i + 1) % numSegments) * segmentSize);
}
return hashes;
}
@Override
public List locateOwnersForSegment(int segmentId) {
return segmentOwners[segmentId];
}
@Override
public Address locatePrimaryOwnerForSegment(int segmentId) {
return segmentOwners[segmentId].get(0);
}
@Override
public List getMembers() {
return members;
}
@Override
public int getNumOwners() {
return numOwners;
}
@Override
public Address locatePrimaryOwner(Object key) {
return locatePrimaryOwnerForSegment(getSegment(key));
}
@Override
public List locateOwners(Object key) {
return locateOwnersForSegment(getSegment(key));
}
@Override
public Set locateAllOwners(Collection
© 2015 - 2025 Weber Informatics LLC | Privacy Policy