package resa.optimize;
import backtype.storm.Config;
import backtype.storm.generated.StormTopology;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import resa.util.ConfigUtil;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
/**
* Created by ding on 14-4-30.
*/
public class SimpleChainAllocCalculator extends AllocCalculator {
private static final Logger LOG = LoggerFactory.getLogger(SimpleChainAllocCalculator.class);
private AggregatedData spoutAregatedData;
private AggregatedData boltAregatedData;
private int historySize;
private int currHistory;
@Override
public void init(Map<String, Object> conf, Map<String, Integer> currAllocation, StormTopology rawTopology) {
super.init(conf, currAllocation, rawTopology);
historySize = ConfigUtil.getInt(conf, "resa.opt.win.history.size", 1);
currHistory = 0;
spoutAregatedData = new AggregatedData(rawTopology, historySize);
boltAregatedData = new AggregatedData(rawTopology, historySize);
}
@Override
public AllocResult calc(Map<String, AggResult[]> executorAggResults, int maxAvailableExecutors) {
executorAggResults.entrySet().stream().filter(e -> rawTopology.get_spouts().containsKey(e.getKey()))
.forEach(e -> spoutAregatedData.putResult(e.getKey(), e.getValue()));
executorAggResults.entrySet().stream().filter(e -> rawTopology.get_bolts().containsKey(e.getKey()))
.forEach(e -> boltAregatedData.putResult(e.getKey(), e.getValue()));
// check history size. Ensure we have enough history data before we run the optimize function
currHistory++;
if (currHistory < historySize) {
LOG.info("currHistory < historySize, curr: " + currHistory + ", Size: " + historySize);
return null;
} else {
currHistory = historySize;
}
///Temp use, assume only one running topology!
double targetQoSMs = ConfigUtil.getDouble(conf, "resa.opt.smd.qos.ms", 5000.0);
int maxSendQSize = ConfigUtil.getInt(conf, Config.TOPOLOGY_EXECUTOR_SEND_BUFFER_SIZE, 1024);
int maxRecvQSize = ConfigUtil.getInt(conf, Config.TOPOLOGY_EXECUTOR_RECEIVE_BUFFER_SIZE, 1024);
double sendQSizeThresh = ConfigUtil.getDouble(conf, "resa.opt.smd.sq.thresh", 5.0);
double recvQSizeThreshRatio = ConfigUtil.getDouble(conf, "resa.opt.smd.rq.thresh.ratio", 0.6);
double recvQSizeThresh = recvQSizeThreshRatio * maxRecvQSize;
double avgCompleteHis = spoutAregatedData.compHistoryResults.entrySet().stream().mapToDouble(e -> {
Iterable<AggResult> results = e.getValue();
SpoutAggResult hisCar = AggResult.getCombinedResult(new SpoutAggResult(), results);
CntMeanVar hisCarCombined = hisCar.getCombinedCompletedLatency();
return hisCarCombined.getAvg();
}).average().getAsDouble();
Map<String, ServiceNode> queueingNetwork = boltAregatedData.compHistoryResults.entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey, e -> {
Iterable<AggResult> results = e.getValue();
BoltAggResult hisCar = AggResult.getCombinedResult(new BoltAggResult(), results);
CntMeanVar hisCarCombined = hisCar.getCombinedProcessedResult();
double avgSendQLenHis = hisCar.getSendQueueResult().getAvgQueueLength();
double avgRecvQLenHis = hisCar.getRecvQueueResult().getAvgQueueLength();
double arrivalRateHis = hisCar.getArrivalRatePerSec();
double avgServTimeHis = hisCarCombined.getAvg();///unit is millisecond
double lambdaHis = arrivalRateHis * currAllocation.get(e.getKey());
double muHis = 1000.0 / avgServTimeHis;
//TODO: when processed tuple count is very small (e.g. there is no input tuple),
// avgServTime becomes zero and mu becomes infinity, this will cause problematic SN.
double rhoHis = lambdaHis / muHis;
boolean sendQLenNormalHis = avgSendQLenHis < sendQSizeThresh;
boolean recvQlenNormalHis = avgRecvQLenHis < recvQSizeThresh;
LOG.info("avgSQLenHis: " + avgSendQLenHis + ",avgRQLenHis: " + avgRecvQLenHis + ", arrRateHis: "
+ arrivalRateHis + ", avgServTimeHis(ms): " + avgServTimeHis);
LOG.info("rhoHis: " + rhoHis + ", lambdaHis: " + lambdaHis + ", muHis: " + muHis);
return new ServiceNode(lambdaHis, muHis, ServiceNode.ServiceType.EXPONENTIAL, 1);
}));
int maxThreadAvailable4Bolt = maxAvailableExecutors - currAllocation.entrySet().stream()
.filter(e -> rawTopology.get_spouts().containsKey(e.getKey()))
.mapToInt(Map.Entry::getValue).sum();
Map<String, Integer> boltAllocation = currAllocation.entrySet().stream()
.filter(e -> rawTopology.get_bolts().containsKey(e.getKey()))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
AllocResult allocResult = SimpleGeneralServiceModel.checkOptimized(queueingNetwork, avgCompleteHis,
targetQoSMs, boltAllocation, maxThreadAvailable4Bolt);
Map<String, Integer> retCurrAllocation = new HashMap<>(currAllocation);
// merge the optimized decision into source allocation
retCurrAllocation.putAll(allocResult.currOptAllocation);
LOG.info(currAllocation + "-->" + retCurrAllocation);
LOG.info("minReq: {} " + allocResult.minReqOptAllocation + ", status: " + allocResult.status);
Map<String, Integer> retMinReqAllocation = null;
if (allocResult.minReqOptAllocation != null) {
retMinReqAllocation = new HashMap<>(currAllocation);
// merge the optimized decision into source allocation
retMinReqAllocation.putAll(allocResult.minReqOptAllocation);
}
return new AllocResult(allocResult.status, retMinReqAllocation, retCurrAllocation);
}
@Override
public void allocationChanged(Map<String, Integer> newAllocation) {
super.allocationChanged(newAllocation);
spoutAregatedData.clear();
boltAregatedData.clear();
currHistory = 0;
}
}