
org.infinispan.distribution.ch.impl.ReplicatedConsistentHash Maven / Gradle / Ivy
package org.infinispan.distribution.ch.impl;
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.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.function.UnaryOperator;
import org.infinispan.commons.hash.Hash;
import org.infinispan.commons.marshall.InstanceReusingAdvancedExternalizer;
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.topology.PersistentUUID;
import org.infinispan.commons.util.SmallIntSet;
import org.infinispan.util.RangeSet;
/**
* Special implementation of {@link org.infinispan.distribution.ch.ConsistentHash} for replicated caches.
* The hash-space has several segments owned by all members and the primary ownership of each segment is evenly
* spread between members.
*
* @author Dan Berindei
* @author [email protected]
* @since 5.2
*/
public class ReplicatedConsistentHash implements ConsistentHash {
private static final String STATE_PRIMARY_OWNERS = "primaryOwners.%d";
private static final String STATE_PRIMARY_OWNERS_COUNT = "primaryOwners";
private final Hash hashFunction;
private final int[] primaryOwners;
private final List members;
private final Set membersSet;
private final Set segments;
private final int segmentSize;
public ReplicatedConsistentHash(Hash hashFunction, List members, int[] primaryOwners) {
this.hashFunction = hashFunction;
this.members = Collections.unmodifiableList(new ArrayList<>(members));
this.membersSet = Collections.unmodifiableSet(new HashSet<>(members));
this.primaryOwners = primaryOwners;
segments = new RangeSet(primaryOwners.length);
segmentSize = Util.getSegmentSize(primaryOwners.length);
}
public ReplicatedConsistentHash union(ReplicatedConsistentHash ch2) {
if (!this.getHashFunction().equals(ch2.getHashFunction())) {
throw new IllegalArgumentException("The consistent hash objects must have the same hash function");
}
if (this.getNumSegments() != ch2.getNumSegments()) {
throw new IllegalArgumentException("The consistent hash objects must have the same number of segments");
}
List unionMembers = new ArrayList<>(this.getMembers());
for (Address member : ch2.getMembers()) {
if (!unionMembers.contains(member)) {
unionMembers.add(member);
}
}
int[] primaryOwners = new int[this.getNumSegments()];
for (int segmentId = 0; segmentId < primaryOwners.length; segmentId++) {
Address primaryOwner = this.locatePrimaryOwnerForSegment(segmentId);
int primaryOwnerIndex = unionMembers.indexOf(primaryOwner);
primaryOwners[segmentId] = primaryOwnerIndex;
}
return new ReplicatedConsistentHash(this.getHashFunction(), unionMembers, primaryOwners);
}
ReplicatedConsistentHash(ScopedPersistentState state) {
this.hashFunction = Util.getInstance(state.getProperty(ConsistentHashPersistenceConstants.STATE_HASH_FUNCTION), null);
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)));
this.members.add(uuid);
}
this.membersSet = Collections.unmodifiableSet(new HashSet<>(this.members));
int numPrimaryOwners = state.getIntProperty(STATE_PRIMARY_OWNERS_COUNT);
this.primaryOwners = new int[numPrimaryOwners];
for (int i = 0; i < numPrimaryOwners; i++) {
this.primaryOwners[i] = state.getIntProperty(String.format(STATE_PRIMARY_OWNERS, i));
}
segments = new RangeSet(primaryOwners.length);
segmentSize = Util.getSegmentSize(primaryOwners.length);
}
@Override
public int getNumSegments() {
return primaryOwners.length;
}
@Override
public int getNumOwners() {
return members.size();
}
@Override
public List getMembers() {
return members;
}
@Override
public Hash getHashFunction() {
return hashFunction;
}
@Override
public int getSegment(Object key) {
// The result must always be positive, so we make sure the dividend is positive first
return (hashFunction.hash(key) & Integer.MAX_VALUE) / segmentSize;
}
@Override
public List locateOwnersForSegment(int segmentId) {
Address primaryOwner = locatePrimaryOwnerForSegment(segmentId);
List owners = new ArrayList<>(members.size());
owners.add(primaryOwner);
for (Address member : members) {
if (!member.equals(primaryOwner)) {
owners.add(member);
}
}
return owners;
}
@Override
public Address locatePrimaryOwnerForSegment(int segmentId) {
return members.get(primaryOwners[segmentId]);
}
@Override
public Set getSegmentsForOwner(Address owner) {
if (owner == null) {
throw new IllegalArgumentException("owner cannot be null");
}
if (!membersSet.contains(owner)) {
throw new IllegalArgumentException("The node is not a member : " + owner);
}
return segments;
}
@Override
public Set getPrimarySegmentsForOwner(Address owner) {
int index = members.indexOf(owner);
if (index == -1) {
throw new IllegalArgumentException("The node is not a member : " + owner);
}
Set primarySegments = new SmallIntSet(primaryOwners.length);
for (int i = 0; i < primaryOwners.length; ++i) {
if (primaryOwners[i] == index) {
primarySegments.add(i);
}
}
return primarySegments;
}
@Override
public String getRoutingTableAsString() {
StringBuilder sb = new StringBuilder();
for (Address a : members) {
if (sb.length() > 0) {
sb.append("\n ");
}
Set primarySegments = getPrimarySegmentsForOwner(a);
sb.append(a).append(" primary: ").append(primarySegments);
}
return sb.toString();
}
@Override
public Set locateAllOwners(Collection
© 2015 - 2025 Weber Informatics LLC | Privacy Policy