package me.prettyprint.cassandra.connection;
import me.prettyprint.cassandra.connection.factory.HClientFactory;
import me.prettyprint.cassandra.service.CassandraHost;
import me.prettyprint.cassandra.service.CassandraClientMonitor;
import java.util.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.collect.Lists;
/**
* Selects the least active host based on the number of active connections.
* The list of hosts is shuffled on each pass to account for the case
* where a number of hosts are at the minimum number of connections
* (ie. they are not busy).
*
*
* @author zznate
*/
public class LeastActiveBalancingPolicy implements LoadBalancingPolicy {
private static final long serialVersionUID = 329849818218657061L;
private static final Logger log = LoggerFactory.getLogger(LeastActiveBalancingPolicy.class);
@Override
public HClientPool getPool(Collection<HClientPool> pools, Set<CassandraHost> excludeHosts) {
List<HClientPool> vals = Lists.newArrayList(pools);
// shuffle pools to avoid always returning the same one when we are not terribly busy
Collections.shuffle(vals);
Collections.sort(vals, new ShufflingCompare());
Iterator<HClientPool> iterator = vals.iterator();
HClientPool concurrentHClientPool = iterator.next();
if ( excludeHosts != null && excludeHosts.size() > 0 ) {
while (iterator.hasNext()) {
if ( !excludeHosts.contains(concurrentHClientPool.getCassandraHost()) ) {
break;
}
concurrentHClientPool = (ConcurrentHClientPool) iterator.next();
}
}
return concurrentHClientPool;
}
/**
* Make the results of this Comparator stable (and thus transitive) by caching the numActive value
* for each HClientPool as they are seen, then reusing the cached value instead of the current value
* (which may have changed) if the same pool is compared again.
*
* Without this change the new TimSort algorithm in Java 7 sometimes throws a:
* java.lang.IllegalArgumentException: Comparison method violates its general contract!
*/
static final class ShufflingCompare implements Comparator<HClientPool> {
private Map<HClientPool, Integer> cachedActive = new HashMap<HClientPool, Integer>();
public int compare(HClientPool o1, HClientPool o2) {
if ( log.isDebugEnabled() ) {
log.debug("comparing 1: {} and count {} with 2: {} and count {}",
new Object[]{o1.getCassandraHost(), o1.getNumActive(), o2.getCassandraHost(), o2.getNumActive()});
}
return getNumActive(o1) - getNumActive(o2);
}
private int getNumActive(HClientPool p) {
Integer ret = cachedActive.get(p);
if (ret == null) {
ret = p.getNumActive();
cachedActive.put(p, ret);
}
return ret;
}
}
@Override
public HClientPool createConnection(HClientFactory clientFactory, CassandraHost host, CassandraClientMonitor monitor) {
return new ConcurrentHClientPool(clientFactory, host, monitor);
}
}