496 lines
19 KiB
C++
496 lines
19 KiB
C++
/*
|
|
* File: DynamicPeerPlacingStrategy.cc
|
|
* Author: stubbfel
|
|
*
|
|
* Created on 13. Juli 2013, 14:37
|
|
*/
|
|
|
|
#include "DynamicPeerPlacingStrategy.h"
|
|
#include "metric/ClusterSizeBalanceMetric.h"
|
|
#include "metric/ResourceValueBalanceMetric.h"
|
|
#include "metric/ResourceClusterSizeBalanceMetric.h"
|
|
#include "ms/MemberRegister.h"
|
|
#include "common/Exception.h"
|
|
#include "ms/PeerState.h"
|
|
#include "Dispatcher.h"
|
|
#include <math.h>
|
|
|
|
#define DEBUG(msg) MOV_DEBUG<<" "<<msg<<std::endl;
|
|
|
|
namespace ubeeme {
|
|
namespace moversight {
|
|
|
|
const size_t DynamicPeerPlacingStrategy::ONE_MORE_PEER_OFFSET;
|
|
const size_t DynamicPeerPlacingStrategy::CURRENTPEERCOUNT_OFFSET;
|
|
|
|
/**
|
|
* @brief Constructor
|
|
* @param aRegister - reference of a certain memberregister
|
|
* @param (optional) metricType - type of the balance metric, default is the BALANCEMETRIC_CLUSTERSIZE
|
|
*/
|
|
DynamicPeerPlacingStrategy::DynamicPeerPlacingStrategy(MemberRegister& aRegister, PeerPlacingStrategyMetricType metricType) : PeerPlacingStrategy(aRegister), metric(NULL) {
|
|
setPlacingMetric(metricType);
|
|
}
|
|
|
|
/**
|
|
* @brief Copy-Constructor
|
|
* @param other : DynamicPeerPlacingStrategy
|
|
*/
|
|
DynamicPeerPlacingStrategy::DynamicPeerPlacingStrategy(const DynamicPeerPlacingStrategy & other) : PeerPlacingStrategy(other.memRegister) {
|
|
operator=(other);
|
|
}
|
|
|
|
/** Assignment operator
|
|
* @param other Object to assign from
|
|
* @return A reference to this
|
|
*/
|
|
DynamicPeerPlacingStrategy &
|
|
DynamicPeerPlacingStrategy::operator=(const DynamicPeerPlacingStrategy& other) {
|
|
if (this == &other) {
|
|
return *this;
|
|
}
|
|
|
|
metric = other.metric->clone();
|
|
metricType = other.metricType;
|
|
return *this;
|
|
}
|
|
|
|
/**
|
|
* Destructor
|
|
*/
|
|
DynamicPeerPlacingStrategy::~DynamicPeerPlacingStrategy() {
|
|
delete metric;
|
|
metric = NULL;
|
|
}
|
|
|
|
/**
|
|
* @brief overrides placePeer from @see PeerPlacingStrategy
|
|
* @param peer - peer which has to be insert in the network
|
|
*/
|
|
void
|
|
DynamicPeerPlacingStrategy::placePeer(Peer & peer) {
|
|
ClusterID clusterID;
|
|
const ClusterList & clusterList = memRegister.getClusters();
|
|
|
|
// check if a new cluster is necessary
|
|
if (isNewClusterRequired(clusterList, ONE_MORE_PEER_OFFSET)) {
|
|
clusterID = memRegister.createCluster();
|
|
}
|
|
else {
|
|
const Cluster & peerLocation = determinePeerLocation(clusterList, peer);
|
|
clusterID = peerLocation.getClusterID();
|
|
}
|
|
|
|
memRegister.addPeer(peer, clusterID);
|
|
const Cluster & cluster = memRegister.getClusters().getByID(clusterID);
|
|
PeerID oldMasterID = cluster.getMasterID();
|
|
PeerID masterID = metric->getBestMasterID(cluster);
|
|
|
|
if (masterID != oldMasterID) {
|
|
memRegister.selectMaster(clusterID, masterID);
|
|
}
|
|
|
|
balanceNetwork();
|
|
}
|
|
|
|
/**
|
|
* @brief overrides getClusterID from @see PeerPlacingStrategy
|
|
* @param pId - id of the peer
|
|
* @param srcClusterId - id of the source cluster
|
|
* @param destClusterId - id of the destination cluster
|
|
* @return True, if the move possible, false otherwise.
|
|
*/
|
|
bool
|
|
DynamicPeerPlacingStrategy::isMovePeerPossible(PeerID pId, ClusterID srcClusterId, ClusterID destClusterId) {
|
|
const ClusterList & clusterList = memRegister.getClusters();
|
|
|
|
if (clusterList.contains(srcClusterId) && clusterList.contains(destClusterId) && memRegister.contains(pId)) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @brief overides getClusterID from @see PeerPlacingStrategy
|
|
*/
|
|
ClusterID
|
|
DynamicPeerPlacingStrategy::getClusterID() {
|
|
return memRegister.getLocalPeer().getClusterID();
|
|
}
|
|
|
|
/**
|
|
* @brief Method determine the location(cluster) of a peer
|
|
* @param clusterList - list of possible peer locations
|
|
* @param peer - peer which look for new location
|
|
* @return Cluster - possible new location of the peer
|
|
*/
|
|
Cluster
|
|
DynamicPeerPlacingStrategy::determinePeerLocation(const ClusterList & clusterList, Peer & peer) const {
|
|
// creates and sort cluster lists
|
|
metric->setupMetric(clusterList);
|
|
ClusterID clusterID = metric->getBestClusterIDForNewPeer();
|
|
return clusterList.getByID(clusterID);
|
|
}
|
|
|
|
/**
|
|
* @brief overrides determinePeerLocation from @see PeerPlacingStrategy. This class dont use this method
|
|
* @param csList The current group situation, described as cluster density.
|
|
* @return A cluster size object, which describes the best cluster to store the peer.
|
|
* The size of the cluster size object describes the situation before performing the peer placement.
|
|
*/
|
|
ClusterSize
|
|
DynamicPeerPlacingStrategy::determinePeerLocation(ClusterSizeList & csList) {
|
|
return csList.get(0);
|
|
}
|
|
|
|
/**
|
|
* @brief overrides determinePossibleNewMaster from @see PeerPlacingStrategy
|
|
* @param cID - id of the cluster, which look for a new Master
|
|
* @return Id of the new Master
|
|
*/
|
|
PeerID
|
|
DynamicPeerPlacingStrategy::determinePossibleNewMaster(ClusterID cID) {
|
|
const Cluster & cluster = memRegister.getCluster(cID);
|
|
PeerID masterID = cluster.getMasterID();
|
|
|
|
if (cluster.size() > BalanceMetric::SINGLEPEER_CLUSTER) {
|
|
// remove old master
|
|
Cluster workCopy(cluster);
|
|
workCopy.removePeer(masterID);
|
|
PeerID newMasterID = metric->getBestMasterID(workCopy);
|
|
|
|
if (newMasterID != UNDEFINED_PEER_ID) {
|
|
return newMasterID;
|
|
}
|
|
}
|
|
|
|
return masterID;
|
|
}
|
|
|
|
/**
|
|
* @brief method calculate the optimal Count of the froup depends of the peercount
|
|
* @param peerCount, peer count of a group
|
|
*/
|
|
size_t
|
|
DynamicPeerPlacingStrategy::calcOptimalClusterCount(size_t peerCount) const {
|
|
return static_cast<int> (sqrt(peerCount) + BalanceMetric::ROUND_HALF_UP);
|
|
}
|
|
|
|
/**
|
|
* @brief Method calculate if an new cluster is necessary
|
|
* @param clusterList - list of clusters
|
|
* @param peerCountOffset - useful, if for you want to know is a new cluster
|
|
* nessaccery when i add 2 more peer.
|
|
* @return true if a new cluster is necessary , otherwise false
|
|
*/
|
|
bool
|
|
DynamicPeerPlacingStrategy::isNewClusterRequired(const ClusterList & clusterList, size_t peerCountOffset) const {
|
|
// calc cluster and peercount
|
|
size_t peerCount = ClusterSizeBalanceMetric::calcPeerCount(clusterList) + peerCountOffset;
|
|
size_t clusterCount = clusterList.size();
|
|
// calc best cluster count
|
|
size_t optClusterCount = calcOptimalClusterCount(peerCount);
|
|
|
|
if (clusterCount == BalanceMetric::EMPTY_CLUSTER || optClusterCount > clusterCount) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @brief Method calculute if its too many cluster in the clusternetwork
|
|
* @param clusterList - list of clusters
|
|
* @return true, if to many cluster in the clusterlist, otherwise false
|
|
*/
|
|
bool
|
|
DynamicPeerPlacingStrategy::isTooManyCluster(const ClusterList & clusterList) const {
|
|
// calc cluster and peercount
|
|
size_t peerCount = ClusterSizeBalanceMetric::calcPeerCount(clusterList);
|
|
size_t clusterCount = clusterList.size();
|
|
// calc best cluster count
|
|
size_t optClusterCount = calcOptimalClusterCount(peerCount);
|
|
|
|
if (optClusterCount < clusterCount) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @brief overrides clone from @see PeerPlacingStrategy
|
|
* @return A copy instance of the current PeerPlacingStrategy
|
|
*/
|
|
PeerPlacingStrategy *
|
|
DynamicPeerPlacingStrategy::clone() const {
|
|
return new DynamicPeerPlacingStrategy(*this);
|
|
}
|
|
|
|
/**
|
|
* @brief method excute a loadbalancing
|
|
*/
|
|
void
|
|
DynamicPeerPlacingStrategy::balanceNetwork() const {
|
|
size_t maxNumberBalanceRound = calcMaxNumberBalanceRound();
|
|
forwardBalanceNetwork(maxNumberBalanceRound);
|
|
|
|
if (metric->canBackwardBalance()) {
|
|
backwardBalanceNetwork(maxNumberBalanceRound);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief method calculate maxium number of balance round
|
|
* @return maxium number of balance round
|
|
*/
|
|
size_t
|
|
DynamicPeerPlacingStrategy::calcMaxNumberBalanceRound() const {
|
|
const ClusterList & clusterList = memRegister.getClusters();
|
|
size_t peerCount = ClusterSizeBalanceMetric::calcPeerCount(clusterList);
|
|
return peerCount - clusterList.size();
|
|
}
|
|
|
|
/**
|
|
* @brief method excute a loadbalancing, mv peer froom less loaded cluster to most loaded cluster
|
|
* @param maxBalanceRound - the maxium number of balance round
|
|
*/
|
|
void
|
|
DynamicPeerPlacingStrategy::forwardBalanceNetwork(size_t & maxBalanceRound) const {
|
|
for (size_t i = 0; i < maxBalanceRound; i++) {
|
|
const ClusterList & clusterList = memRegister.getClusters();
|
|
// creates and sort cluster lists
|
|
metric->setupMetric(clusterList);
|
|
const ClusterList & ucList = metric->getUcList();
|
|
const ClusterList & ncList = metric->getNcList();
|
|
const ClusterList & ocList = metric->getOcList();
|
|
|
|
// check for overlouded cluster
|
|
if (ocList.size() < BalanceMetric::SINGLEPEER_CLUSTER) {
|
|
break;
|
|
}
|
|
|
|
// determine srcCluster
|
|
ClusterID srcClusterID = metric->getMostLoadedClusterID(ocList);
|
|
ClusterID dstClusterID;
|
|
|
|
// determine dstCluster
|
|
if (ucList.size() > BalanceMetric::EMPTY_CLUSTER) {
|
|
dstClusterID = metric->getLessLoadedClusterID(ucList);
|
|
}
|
|
else if (ncList.size() > BalanceMetric::EMPTY_CLUSTER) {
|
|
dstClusterID = metric->getLessLoadedClusterID(ncList);
|
|
}
|
|
else {
|
|
dstClusterID = metric->getLessLoadedClusterID(ocList);
|
|
}
|
|
|
|
Cluster srcCluster(clusterList.getByID(srcClusterID));
|
|
const Cluster & dstCluster = clusterList.getByID(dstClusterID);
|
|
|
|
// check if its balenceable
|
|
if (!metric->isLoadBalancingPossible(srcCluster, dstCluster)) {
|
|
break;
|
|
}
|
|
|
|
PeerID srcMasterID = srcCluster.getMasterID();
|
|
PeerID mvPeerID;
|
|
|
|
// determine movepeer
|
|
while (srcCluster.size() > BalanceMetric::EMPTY_CLUSTER) {
|
|
mvPeerID = metric->getMostLoadedPeerID(srcCluster);
|
|
|
|
if (srcMasterID == mvPeerID) {
|
|
srcCluster.removePeer(mvPeerID);
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
memRegister.movePeer(mvPeerID, dstClusterID);
|
|
maxBalanceRound--;
|
|
// set newMaster
|
|
PeerID masterID = metric->getBestMasterID(dstCluster);
|
|
memRegister.selectMaster(dstClusterID, masterID);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief method excute a loadbalancing, mv peer froom most loaded cluster to less loaded cluster
|
|
* @param maxBalanceRound - the maxium number of balance round
|
|
*/
|
|
void
|
|
DynamicPeerPlacingStrategy::backwardBalanceNetwork(size_t & maxBalanceRound) const {
|
|
for (size_t i = 0; i < maxBalanceRound; i++) {
|
|
const ClusterList & clusterList = memRegister.getClusters();
|
|
// creates and sort cluster lists
|
|
metric->setupMetric(clusterList);
|
|
const ClusterList & ucList = metric->getUcList();
|
|
const ClusterList & ncList = metric->getNcList();
|
|
const ClusterList & ocList = metric->getOcList();
|
|
|
|
if (ucList.size() < BalanceMetric::SINGLEPEER_CLUSTER) {
|
|
return;
|
|
}
|
|
|
|
// determine srcCluster
|
|
ClusterID srcClusterID = metric->getLessLoadedClusterID(ucList);
|
|
ClusterID dstClusterID;
|
|
|
|
// determine dstCluster
|
|
if (ocList.size() > BalanceMetric::EMPTY_CLUSTER) {
|
|
dstClusterID = metric->getMostLoadedClusterID(ocList);
|
|
}
|
|
else if (ncList.size() > BalanceMetric::EMPTY_CLUSTER) {
|
|
dstClusterID = metric->getMostLoadedClusterID(ncList);
|
|
}
|
|
else {
|
|
dstClusterID = metric->getMostLoadedClusterID(ucList);
|
|
}
|
|
|
|
Cluster srcCluster(clusterList.getByID(srcClusterID));
|
|
const Cluster & dstCluster = clusterList.getByID(dstClusterID);
|
|
|
|
// check if its balenceable
|
|
if (!metric->isLoadBalancingPossible(srcCluster, dstCluster)) {
|
|
return;
|
|
}
|
|
|
|
PeerID srcMasterID = srcCluster.getMasterID();
|
|
PeerID mvPeerID;
|
|
|
|
// determine movepeer
|
|
while (srcCluster.size() > BalanceMetric::EMPTY_CLUSTER) {
|
|
mvPeerID = metric->getLessLoadedPeerID(srcCluster);
|
|
|
|
if (srcMasterID == mvPeerID && srcCluster.size() > BalanceMetric::SINGLEPEER_CLUSTER) {
|
|
srcCluster.removePeer(mvPeerID);
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
memRegister.movePeer(mvPeerID, dstClusterID);
|
|
maxBalanceRound--;
|
|
// set newMaster
|
|
PeerID masterID = metric->getBestMasterID(dstCluster);
|
|
memRegister.selectMaster(dstClusterID, masterID);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Method delete and balance the clusternetwork, if a peer has left the group
|
|
*/
|
|
void
|
|
DynamicPeerPlacingStrategy::peerLeft() const {
|
|
// get current clusterlist
|
|
const ClusterList & clusterList = memRegister.getClusters();
|
|
|
|
if (isTooManyCluster(clusterList)) {
|
|
//delete srcCluster
|
|
ClusterID srcClusterID = metric->getLessLoadedClusterID(clusterList);
|
|
deleteCluster(clusterList, srcClusterID);
|
|
}
|
|
else {
|
|
// add a new cluster
|
|
if (isNewClusterRequired(clusterList, CURRENTPEERCOUNT_OFFSET)) {
|
|
addCluster(clusterList);
|
|
}
|
|
}
|
|
|
|
// balance the network
|
|
balanceNetwork();
|
|
}
|
|
|
|
/**
|
|
* @brief Method a new cluster to the network and move one peer to them
|
|
*/
|
|
void
|
|
DynamicPeerPlacingStrategy::addCluster(const ClusterList & clusterList) const {
|
|
ClusterID newClusterID = memRegister.createCluster();
|
|
ClusterID oldClusterID = metric->getMostLoadedClusterID(clusterList);
|
|
const Cluster & oldCluster = clusterList.getByID(oldClusterID);
|
|
|
|
if (oldCluster.size() <= BalanceMetric::SINGLEPEER_CLUSTER) {
|
|
memRegister.getClusters().remove(newClusterID);
|
|
ClusterList workcopyClusterList(clusterList);
|
|
workcopyClusterList.remove(oldClusterID);
|
|
|
|
if (workcopyClusterList.size() > BalanceMetric::EMPTY_CLUSTER) {
|
|
addCluster(workcopyClusterList);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
PeerID mvPeerID = metric->getMostLoadedPeerID(oldCluster);
|
|
// move Peer
|
|
memRegister.movePeer(mvPeerID, newClusterID);
|
|
memRegister.selectMaster(newClusterID, mvPeerID);
|
|
}
|
|
|
|
/**
|
|
* @brief Method deletes a certain cluster and moves the peers to the other cluster
|
|
* @param clusterList - list of clusters, where the peers of the deleted cluster can be moved
|
|
* @param delClusterId - id of the cluster, which has to be deleted
|
|
*/
|
|
void
|
|
DynamicPeerPlacingStrategy::deleteCluster(const ClusterList & clusterList, ClusterID delClusterId) const {
|
|
|
|
// create workcopy
|
|
ClusterList workcopy(clusterList);
|
|
Cluster cluster = workcopy.getByID(delClusterId);
|
|
// remove cluster
|
|
workcopy.remove(delClusterId);
|
|
ClusterID dstClusterID = metric->getBestClusterIDForNewPeer(workcopy);
|
|
PeerID mvPeerID;
|
|
|
|
while (cluster.size() > BalanceMetric::EMPTY_CLUSTER) {
|
|
mvPeerID = metric->getMostLoadedPeerID(cluster);
|
|
// move Peer
|
|
memRegister.movePeer(mvPeerID, dstClusterID);
|
|
// refresh workcopy
|
|
workcopy.addPeer(cluster.getPeer(mvPeerID), dstClusterID);
|
|
cluster.removePeer(mvPeerID);
|
|
dstClusterID = metric->getBestClusterIDForNewPeer(workcopy);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Method set the PlacingMetric
|
|
* @param metricType - type of the balance metric, default is the BALANCEMETRIC_CLUSTERSIZE
|
|
*/
|
|
void
|
|
DynamicPeerPlacingStrategy::setPlacingMetric(PeerPlacingStrategyMetricType metricType) {
|
|
|
|
if (metric != NULL) {
|
|
delete metric;
|
|
}
|
|
|
|
switch (metricType) {
|
|
case BALANCEMETRIC_CLUSTERSIZE:
|
|
metric = new ClusterSizeBalanceMetric();
|
|
break;
|
|
|
|
case BALANCEMETRIC_RESOURCEVALUECLUSTERSIZE:
|
|
metric = new ResourceClusterSizeBalanceMetric();
|
|
break;
|
|
|
|
case BALANCEMETRIC_RESOURCEVALUE:
|
|
metric = new ResourceValueBalanceMetric();
|
|
break;
|
|
|
|
default:
|
|
metric = new ClusterSizeBalanceMetric();
|
|
break;
|
|
}
|
|
|
|
metricType = metricType;
|
|
}
|
|
|
|
}
|
|
}
|