package com.rayo.storage.lb; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.locks.ReentrantLock; import org.apache.commons.lang.builder.ToStringBuilder; import org.apache.commons.lang.builder.ToStringStyle; import com.rayo.server.storage.model.RayoNode; public class NodeSet { class Entry { RayoNode node; int hits; double expected; double factor; double sum; int priority; boolean takeit() { if (priority != node.getPriority()) { return false; } sum+=factor; if (sum >= hits + 1) { hits++; return true; } return false; } @Override public String toString() { return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE) .append("hits", hits) .append("factor", factor) .append("sum", sum) .toString(); } } private List<Entry> entries = new ArrayList<Entry>(); private Map<String,Entry> nodeKeys = new ConcurrentHashMap<String, Entry>(); private int currentEntry = 0; private ReentrantLock lock = new ReentrantLock(); public RayoNode next(List<RayoNode> nodes) { lock.lock(); try { boolean recalculate = false; for (RayoNode node: nodes) { Entry stored = nodeKeys.get(node.getHostname()); if (stored == null) { RayoNode clone = clone(node); Entry entry = new Entry(); entry.expected = node.getWeight(); entry.node = clone; entries.add(entry); nodeKeys.put(node.getHostname(), entry); recalculate = true; } else { if (stored.node.getWeight() != node.getWeight() || stored.node.getPriority() != node.getPriority()) { stored.node.setWeight(node.getWeight()); stored.node.setPriority(node.getPriority()); stored.expected = stored.node.getWeight(); recalculate = true; } } } if (recalculate) { recalculate(); } RayoNode node = loadNextEntry(); while (node != null && !nodes.contains(node) ) { removeNode(node); currentEntry--; recalculate(); node = loadNextEntry(); } return node; } finally { lock.unlock(); } } private RayoNode clone(RayoNode node) { RayoNode copy = new RayoNode(); copy.setHostname(node.getHostname()); copy.setIpAddress(node.getIpAddress()); copy.setWeight(node.getWeight()); copy.setPlatforms(new HashSet<String>(node.getPlatforms())); copy.setPriority(node.getPriority()); copy.setConsecutiveErrors(node.getConsecutiveErrors()); copy.setBlackListed(node.isBlackListed()); return copy; } private void removeNode(RayoNode node) { nodeKeys.remove(node.getHostname()); Iterator<Entry> it = entries.iterator(); while (it.hasNext()) { if (it.next().node.equals(node)) { it.remove(); break; } } } private RayoNode loadNextEntry() { if (currentEntry >= entries.size()) { currentEntry = 0; } Entry entry = entries.get(currentEntry); if (entry.takeit()) { currentEntry++; return entry.node; } else { currentEntry++; return loadNextEntry(); } } private void recalculate() { double max = 0; int priority = Integer.MAX_VALUE; for (Entry entry: entries) { if (entry.expected > max) { max = entry.expected; } if (entry.node.getPriority() < priority) { priority = entry.node.getPriority(); } } for (Entry entry: entries) { entry.factor = entry.expected / max; entry.priority = priority; } } }