package com.griddynamics.jagger.invoker;
import com.google.common.collect.AbstractIterator;
import com.griddynamics.jagger.util.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.Random;
import java.util.concurrent.ArrayBlockingQueue;
/**
* This {@link com.griddynamics.jagger.invoker.LoadBalancer} implementation provides guarantees
* that each query and endpoint pair will be in exclusive access, i.e. once it is acquired by one thread
* it won't be acquired by any other thread (virtual user) in multi threaded environment.
* @n
* If {@link #randomnessSeed} is not {@code null} randomly shuffles the sequence of pairs from {@link #pairSupplierFactory} using it.
* @n
* Created by Andrey Badaev
* Date: 01/02/17
*
* @ingroup Main_Distributors_group */
public abstract class ExclusiveAccessLoadBalancer<Q, E> extends PairSupplierFactoryLoadBalancer<Q, E> {
private final static Logger log = LoggerFactory.getLogger(ExclusiveAccessLoadBalancer.class);
public ExclusiveAccessLoadBalancer(PairSupplierFactory<Q, E> pairSupplierFactory) {
super(pairSupplierFactory);
}
private volatile ArrayBlockingQueue<Pair<Q, E>> pairQueue;
private volatile Long randomnessSeed;
public void setRandomnessSeed(Long randomnessSeed) {
this.randomnessSeed = randomnessSeed;
}
protected abstract boolean isToCircleAnIteration();
protected ArrayBlockingQueue<Pair<Q, E>> getPairQueue() {
return pairQueue;
}
protected abstract Pair<Q, E> pollNext();
@Override
public Iterator<Pair<Q, E>> provide() {
return new AbstractIterator<Pair<Q, E>>() {
Pair<Q, E> current = null;
@Override
protected Pair<Q, E> computeNext() {
if (current != null && isToCircleAnIteration()) {
log.debug("Returning pair - {}", current);
pairQueue.add(current);
}
current = pollNext();
log.debug("Providing pair - {}", current);
return current;
}
@Override
public String toString() {
return super.getClass() + " iterator";
}
};
}
@Override
public void init() {
synchronized (lock) {
if (initialized) {
log.debug("already initialized. returning...");
return;
}
super.init();
PairSupplier<Q, E> pairSupplier = getPairSupplier();
List<Pair<Q, E>> pairList = new ArrayList<>(pairSupplier.size());
int index = 0;
int step = 1;
if (kernelInfo != null) { // then from all the provided pairs pick only those under this node's index.
index = kernelInfo.getKernelId();
step = kernelInfo.getKernelsNumber();
}
for (; index < pairSupplier.size(); index += step) {
pairList.add(pairSupplier.get(index));
}
if (Objects.nonNull(randomnessSeed)) {
log.info("'randomnessSeed' value is not null. Going to shuffle the pairs");
Collections.shuffle(pairList, new Random(randomnessSeed));
}
log.info("{} pairs on this node to balance", pairList.size());
log.debug("Pairs to load balance: {}", pairList);
pairQueue = new ArrayBlockingQueue<>(pairSupplier.size(), true, pairList);
}
}
}