package org.radargun.service;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.infinispan.distribution.ch.ConsistentHash;
import org.infinispan.distribution.ch.DefaultConsistentHash;
import org.infinispan.distribution.ch.TopologyInfo;
import org.infinispan.remoting.transport.Address;
import org.radargun.logging.Log;
import org.radargun.logging.LogFactory;
import org.radargun.stages.cache.generators.ObjectKeyGenerator;
/**
* @author Mircea Markus <Mircea.Markus@jboss.com>
*/
public class EvenSpreadingConsistentHash implements ConsistentHash {
private static Log log = LogFactory.getLog(EvenSpreadingConsistentHash.class);
/**
* Why static? because the consistent hash is recreated when cluster changes and there's no other way to pass these
* across
*/
private static volatile int threadCountPerNode = -1;
private static volatile int keysPerThread = -1;
private volatile DefaultConsistentHash existing;
private final List<Address> cachesList = new ArrayList<Address>();
public EvenSpreadingConsistentHash() {//needed for UT
existing = new DefaultConsistentHash();
}
@Override
public List<Address> locate(Object key, int replCount) {
if (!(key instanceof ObjectKeyGenerator.ObjectKey)) {
if (log.isTraceEnabled()) log.trace("Delegating key " + key + " to default CH");
return existing.locate(key, replCount);
}
if (threadCountPerNode <= 0 || keysPerThread <= 0)
throw new IllegalStateException("keysPerThread and threadCountPerNode need to be set!");
Set<Address> caches = existing.getCaches();
int clusterSize = caches.size();
long keyIndexInCluster = getSequenceNumber((ObjectKeyGenerator.ObjectKey) key);
int firstIndex = (int) (keyIndexInCluster % caches.size());
List<Address> result = new ArrayList<Address>();
for (int i = 0; i < replCount; i++) {
Address address = cachesList.get((firstIndex + i) % clusterSize);
result.add(address);
if (result.size() == replCount) break;
}
if (log.isTraceEnabled())
log.trace("Handling key " + key + ", clusterIndex==" + keyIndexInCluster + " and EvenSpreadingConsistentHash --> " + result);
return Collections.unmodifiableList(result);
}
private long getSequenceNumber(ObjectKeyGenerator.ObjectKey key) {
return key.getKeyIndexInCluster(threadCountPerNode, keysPerThread);
}
public void init(int threadCountPerNode, int keysPerThread) {
log.trace("Setting threadCountPerNode =" + threadCountPerNode + " and keysPerThread = " + keysPerThread);
this.threadCountPerNode = threadCountPerNode;
this.keysPerThread = keysPerThread;
}
/**
* No need to implement this: https://issues.jboss.org/browse/ISPN-899
*/
@Override
public int getHashId(Address a) {
return 0;
}
/**
* No need to implement this: https://issues.jboss.org/browse/ISPN-899
*/
@Override
public int getHashSpace() {
return 0;
}
//following methods should only be used during rehashing, so no point in implementing them
@Override
public List<Address> getStateProvidersOnLeave(Address leaver, int replCount) {
return existing.getStateProvidersOnLeave(leaver, replCount);
}
@Override
public List<Address> getStateProvidersOnJoin(Address joiner, int replCount) {
return existing.getStateProvidersOnJoin(joiner, replCount);
}
@Override
public List<Address> getBackupsForNode(Address node, int replCount) {
if (log.isTraceEnabled()) log.trace("getBackupsForNode (" + node + ")");
return existing.getBackupsForNode(node, replCount);
}
@Override
public void setCaches(Set<Address> caches) {
existing.setCaches(caches);
cachesList.addAll(caches);
Collections.sort(cachesList, new Comparator<Address>() {
@Override
public int compare(Address o1, Address o2) {
return o1.toString().compareTo(o2.toString());
}
});
}
@Override
public void setTopologyInfo(TopologyInfo topologyInfo) {
existing.setTopologyInfo(topologyInfo);
}
public Map<Object, List<Address>> locateAll(Collection<Object> keys, int replCount) {
Map<Object, List<Address>> locations = new HashMap<Object, List<Address>>();
for (Object k : keys) locations.put(k, locate(k, replCount));
return locations;
}
public boolean isKeyLocalToAddress(Address a, Object key, int replCount) {
// simple, brute-force impl
return locate(key, replCount).contains(a);
}
@Override
public Set<Address> getCaches() {
return existing.getCaches();
}
}