Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
package brooklyn.policy.loadbalancing;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import brooklyn.location.Location;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Multimaps;
import com.google.common.collect.SetMultimap;
/**
* Standard implementation of {@link BalanceablePoolModel}, providing essential arithmetic for item and container
* workrates and thresholds. See subclasses for specific requirements for migrating items.
*/
public class DefaultBalanceablePoolModel implements BalanceablePoolModel {
private static final Logger LOG = LoggerFactory.getLogger(DefaultBalanceablePoolModel.class);
/*
* Performance comments.
* - Used hprof with LoadBalancingPolicySoakTest.testLoadBalancingManyManyItemsTest (1000 items)
* - Prior to adding containerToItems, it created a new set by iterating over all items.
* This was the biggest percentage of any brooklyn code.
* Hence it's worth duplicating the values, keyed by item and keyed by container.
* - Unfortunately changing threading model (so have a "rebalancer" thread, and a thread that
* processes events to update the model), get ConcurrentModificationException if don't take
* copy of containerToItems.get(node)...
*/
// Concurrent maps cannot have null value; use this to represent when no container is supplied for an item
private static final String NULL_CONTAINER = "null-container";
private final String name;
private final Set containers = Collections.newSetFromMap(new ConcurrentHashMap());
private final Map containerToLowThreshold = new ConcurrentHashMap();
private final Map containerToHighThreshold = new ConcurrentHashMap();
private final Map itemToContainer = new ConcurrentHashMap();
private final SetMultimap containerToItems = Multimaps.synchronizedSetMultimap(HashMultimap.create());
private final Map itemToWorkrate = new ConcurrentHashMap();
private final Set immovableItems = Collections.newSetFromMap(new ConcurrentHashMap());
private volatile double poolLowThreshold = 0;
private volatile double poolHighThreshold = 0;
private volatile double currentPoolWorkrate = 0;
public DefaultBalanceablePoolModel(String name) {
this.name = name;
}
public ContainerType getParentContainer(ItemType item) {
ContainerType result = itemToContainer.get(item);
return (result != NULL_CONTAINER) ? result : null;
}
public Set getItemsForContainer(ContainerType node) {
Set result = containerToItems.get(node);
synchronized (containerToItems) {
return (result != null) ? ImmutableSet.copyOf(result) : Collections.emptySet();
}
}
public Double getItemWorkrate(ItemType item) {
return itemToWorkrate.get(item);
}
@Override public double getPoolLowThreshold() { return poolLowThreshold; }
@Override public double getPoolHighThreshold() { return poolHighThreshold; }
@Override public double getCurrentPoolWorkrate() { return currentPoolWorkrate; }
@Override public boolean isHot() { return !containers.isEmpty() && currentPoolWorkrate > poolHighThreshold; }
@Override public boolean isCold() { return !containers.isEmpty() && currentPoolWorkrate < poolLowThreshold; }
// Provider methods.
@Override public String getName() { return name; }
@Override public int getPoolSize() { return containers.size(); }
@Override public Set getPoolContents() { return containers; }
@Override public String getName(ContainerType container) { return container.toString(); } // TODO: delete?
@Override public Location getLocation(ContainerType container) { return null; } // TODO?
@Override public double getLowThreshold(ContainerType container) {
Double result = containerToLowThreshold.get(container);
return (result != null) ? result : -1;
}
@Override public double getHighThreshold(ContainerType container) {
Double result = containerToHighThreshold.get(container);
return (result != null) ? result : -1;
}
@Override public double getTotalWorkrate(ContainerType container) {
double totalWorkrate = 0;
for (ItemType item : getItemsForContainer(container)) {
Double workrate = itemToWorkrate.get(item);
if (workrate != null)
totalWorkrate += Math.abs(workrate);
}
return totalWorkrate;
}
@Override public Map getContainerWorkrates() {
Map result = new LinkedHashMap();
for (ContainerType node : containers)
result.put(node, getTotalWorkrate(node));
return result;
}
@Override public Map getItemWorkrates(ContainerType node) {
Map result = new LinkedHashMap();
for (ItemType item : getItemsForContainer(node))
result.put(item, itemToWorkrate.get(item));
return result;
}
@Override public boolean isItemMoveable(ItemType item) {
// If don't know about item, then assume not movable; otherwise has this item been explicitly flagged as immovable?
return itemToContainer.containsKey(item) && !immovableItems.contains(item);
}
@Override public boolean isItemAllowedIn(ItemType item, Location location) {
return true; // TODO?
}
// Mutators.
@Override
public void onItemMoved(ItemType item, ContainerType newNode) {
if (!itemToContainer.containsKey(item)) {
// Item may have been deleted; order of events received from different sources
// (i.e. item itself and for itemGroup membership) is non-deterministic.
LOG.info("Balanceable pool model ignoring onItemMoved for unknown item {} to container {}; " +
"if onItemAdded subsequently received will get new container then", item, newNode);
return;
}
ContainerType newNodeNonNull = toNonNullContainer(newNode);
ContainerType oldNode = itemToContainer.put(item, newNodeNonNull);
if (oldNode != null && oldNode != NULL_CONTAINER) containerToItems.remove(oldNode, item);
if (newNode != null) containerToItems.put(newNode, item);
}
@Override
public void onContainerAdded(ContainerType newContainer, double lowThreshold, double highThreshold) {
boolean added = containers.add(newContainer);
if (!added) {
// See LoadBalancingPolicy.onContainerAdded for possible explanation of why can get duplicate calls
LOG.debug("Duplicate container-added event for {}; ignoring", newContainer);
return;
}
containerToLowThreshold.put(newContainer, lowThreshold);
containerToHighThreshold.put(newContainer, highThreshold);
poolLowThreshold += lowThreshold;
poolHighThreshold += highThreshold;
}
@Override
public void onContainerRemoved(ContainerType oldContainer) {
containers.remove(oldContainer);
Double containerLowThreshold = containerToLowThreshold.remove(oldContainer);
Double containerHighThresold = containerToHighThreshold.remove(oldContainer);
poolLowThreshold -= (containerLowThreshold != null ? containerLowThreshold : 0);
poolHighThreshold -= (containerHighThresold != null ? containerHighThresold : 0);
// TODO: assert no orphaned items
}
@Override
public void onItemAdded(ItemType item, ContainerType parentContainer) {
onItemAdded(item, parentContainer, false);
}
@Override
public void onItemAdded(ItemType item, ContainerType parentContainer, boolean immovable) {
// Duplicate calls to onItemAdded do no harm, as long as most recent is most accurate!
// Important that it stays that way for now - See LoadBalancingPolicy.onContainerAdded for explanation.
if (immovable)
immovableItems.add(item);
ContainerType parentContainerNonNull = toNonNullContainer(parentContainer);
ContainerType oldNode = itemToContainer.put(item, parentContainerNonNull);
if (oldNode != null && oldNode != NULL_CONTAINER) containerToItems.remove(oldNode, item);
if (parentContainer != null) containerToItems.put(parentContainer, item);
}
@Override
public void onItemRemoved(ItemType item) {
ContainerType oldNode = itemToContainer.remove(item);
if (oldNode != null && oldNode != NULL_CONTAINER) containerToItems.remove(oldNode, item);
Double workrate = itemToWorkrate.remove(item);
if (workrate != null)
currentPoolWorkrate -= workrate;
immovableItems.remove(item);
}
@Override
public void onItemWorkrateUpdated(ItemType item, double newValue) {
if (hasItem(item)) {
Double oldValue = itemToWorkrate.put(item, newValue);
double delta = ( newValue - (oldValue != null ? oldValue : 0) );
currentPoolWorkrate += delta;
} else {
// Can happen when item removed - get notification of removal and workrate from group and item
// respectively, so can overtake each other
if (LOG.isDebugEnabled()) LOG.debug("Ignoring setting of workrate for unknown item {}, to {}", item, newValue);
}
}
private boolean hasItem(ItemType item) {
return itemToContainer.containsKey(item);
}
// Additional methods for tests.
/**
* Warning: this can be an expensive (time and memory) operation if there are a lot of items/containers.
*/
@VisibleForTesting
public String itemDistributionToString() {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
dumpItemDistribution(new PrintStream(baos));
return new String(baos.toByteArray());
}
@VisibleForTesting
public void dumpItemDistribution() {
dumpItemDistribution(System.out);
}
@VisibleForTesting
public void dumpItemDistribution(PrintStream out) {
for (ContainerType container : getPoolContents()) {
out.println("Container '"+container+"': ");
for (ItemType item : getItemsForContainer(container)) {
Double workrate = getItemWorkrate(item);
out.println("\t"+"Item '"+item+"' ("+workrate+")");
}
}
out.flush();
}
@SuppressWarnings("unchecked")
private ContainerType nullContainer() {
return (ContainerType) NULL_CONTAINER; // relies on erasure
}
private ContainerType toNonNullContainer(ContainerType container) {
return (container != null) ? container : nullContainer();
}
}