org.infinispan.client.hotrod.impl.consistenthash.ConsistentHashV2 Maven / Gradle / Ivy
package org.infinispan.client.hotrod.impl.consistenthash;
import java.net.SocketAddress;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import org.infinispan.client.hotrod.logging.LogFactory;
import org.infinispan.commons.hash.Hash;
import org.infinispan.commons.hash.MurmurHash3;
import org.infinispan.commons.util.Util;
import org.jboss.logging.BasicLogger;
/**
* Version 2 of the ConsistentHash function. Uses MurmurHash3.
*
* @author manik
* @see org.infinispan.commons.hash.MurmurHash3
* @since 5.0
*/
public class ConsistentHashV2 implements ConsistentHash {
private static final BasicLogger log = LogFactory.getLog(ConsistentHashV2.class);
private final SortedMap positions = new TreeMap();
private volatile int[] hashes;
private volatile SocketAddress[] addresses;
private int hashSpace;
private boolean hashSpaceIsMaxInt;
protected final Hash hash;
private int numKeyOwners;
private final Random rnd;
public ConsistentHashV2(Random rnd) {
this(rnd, MurmurHash3.getInstance());
}
public ConsistentHashV2(Random rnd, Hash hash) {
this.rnd = rnd;
this.hash = hash;
}
public ConsistentHashV2() {
this(new Random());
}
@Override
public void init(Map> servers2Hash, int numKeyOwners, int hashSpace) {
for (Map.Entry> entry : servers2Hash.entrySet()) {
SocketAddress addr = entry.getKey();
for (Integer hash : entry.getValue()) {
SocketAddress prev = positions.put(hash, addr);
if (prev != null)
log.debugf("Adding hash (%d) again, this time for %s. Previously it was associated with: %s", hash, addr, prev);
}
}
int hashWheelSize = positions.size();
log.tracef("Positions (%d entries) are: %s", hashWheelSize, positions);
hashes = new int[hashWheelSize];
Iterator it = positions.keySet().iterator();
for (int i = 0; i < hashWheelSize; i++) {
hashes[i] = it.next();
}
addresses = positions.values().toArray(new SocketAddress[hashWheelSize]);
this.hashSpace = hashSpace;
// This is true if we're talking to an instance of Infinispan 5.2 or newer.
this.hashSpaceIsMaxInt = hashSpace == Integer.MAX_VALUE;
this.numKeyOwners = numKeyOwners;
}
@Override
public SocketAddress getServer(Object key) {
int normalisedHashForKey;
if (hashSpaceIsMaxInt) {
normalisedHashForKey = getNormalizedHash(key);
if (normalisedHashForKey == Integer.MAX_VALUE) normalisedHashForKey = 0;
} else {
normalisedHashForKey = getNormalizedHash(key) % hashSpace;
}
int mainOwner = getHashIndex(normalisedHashForKey);
int indexToReturn = mainOwner % hashes.length;
return addresses[indexToReturn];
}
private int getHashIndex(int normalisedHashForKey) {
int result = Arrays.binarySearch(hashes, normalisedHashForKey);
if (result >= 0) {//the normalisedHashForKey has an exact match in the hashes array
return result;
} else {
//see javadoc for Arrays.binarySearch, @return tag in particular
if (result == (-hashes.length - 1)) {
return 0;
} else {
return -result - 1;
}
}
}
private int getIndex() {
return rnd.nextInt(Math.min(numKeyOwners, positions.size()));
}
@Override
public final int getNormalizedHash(Object object) {
return Util.getNormalizedHash(object, hash);
}
@Override
public Map> getSegmentsByServer() {
throw new UnsupportedOperationException();
}
@Override
public Map> getPrimarySegmentsByServer() {
throw new UnsupportedOperationException();
}
}