package me.prettyprint.cassandra.connection;

import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;

import com.google.common.collect.Iterables;

import me.prettyprint.cassandra.service.CassandraHost;

/**
 * Implements a RoundRobin balancing policy based off the contents
 * of the active {@link HClientPool}. If a pool is shutdown by another 
 * thread in the midst of the selection process, we return the pool
 * at position 0
 *
 * @author zznate
 */
public class RoundRobinBalancingPolicy implements LoadBalancingPolicy {

  private static final long serialVersionUID = 1107204068032227079L;
  private AtomicInteger counter;
  
  public RoundRobinBalancingPolicy() {
    counter = new AtomicInteger();
  }
  
  @Override
  public HClientPool getPool(Collection<HClientPool> pools,
      Set<CassandraHost> excludeHosts) {
    HClientPool pool = getPoolSafely(pools);    
    if ( excludeHosts != null && excludeHosts.size() > 0 ) {
      while ( excludeHosts.contains(pool.getCassandraHost()) ) {
        pool = getPoolSafely(pools);
        if ( excludeHosts.size() >= pools.size() )
          break;
      }
    }    
    return pool;
  }
  
  private HClientPool getPoolSafely(Collection<HClientPool> pools) {
    try {
      return Iterables.get(pools, getAndIncrement(pools.size()));
    } catch (IndexOutOfBoundsException e) {
      return pools.iterator().next();
    }       
  }
    
  private int getAndIncrement(int size) {
    counter.compareAndSet(16384, 0);
    return counter.getAndIncrement() % size;    
  }

  @Override
  public HClientPool createConnection(CassandraHost host) {
  	return new ConcurrentHClientPool(host);
  }
}

package me.prettyprint.cassandra.connection;

import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;

import com.google.common.collect.Iterables;

import me.prettyprint.cassandra.service.CassandraHost;

/**
 * Implements a RoundRobin balancing policy based off the contents
 * of the active {@link HClientPool}. If a pool is shutdown by another 
 * thread in the midst of the selection process, we return the pool
 * at position 0
 *
 * @author zznate
 */
public class RoundRobinBalancingPolicy implements LoadBalancingPolicy {

  private static final long serialVersionUID = 1107204068032227079L;
  private int counter;
  
  public RoundRobinBalancingPolicy() {
    counter = 0;
  }
  
  @Override
  public HClientPool getPool(Collection<HClientPool> pools,
      Set<CassandraHost> excludeHosts) {
    HClientPool pool = getPoolSafely(pools);    
    if ( excludeHosts != null && excludeHosts.size() > 0 ) {
      while ( excludeHosts.contains(pool.getCassandraHost()) ) {
        pool = getPoolSafely(pools);
        if ( excludeHosts.size() >= pools.size() )
          break;
      }
    }    
    return pool;
  }
  
  private HClientPool getPoolSafely(Collection<HClientPool> pools) {
    try {
      return Iterables.get(pools, getAndIncrement(pools.size()));
    } catch (IndexOutOfBoundsException e) {
      return pools.iterator().next();
    }       
  }
    
  private int getAndIncrement(int size) {
    int counterToReturn;
    
    // There should not be that much of contention here as
    // the "if" statement plus the increment is executed real fast.
    synchronized (this) {
      if (counter >= 16384) {
        counter = 0;
      }
      counterToReturn = counter++;
    }

    return counterToReturn % size;
  }

  @Override
  public HClientPool createConnection(CassandraHost host) {
  	return new ConcurrentHClientPool(host);
  }
}

Fix issue #224