package org.spotter.ext.detection.continuousViolation.strategies;
import java.util.ArrayList;
import java.util.List;
import org.lpe.common.util.LpeNumericUtils;
import org.lpe.common.util.NumericPair;
import org.lpe.common.util.NumericPairList;
import org.spotter.ext.detection.continuousViolation.IViolationAnalysisStrategy;
import org.spotter.ext.detection.continuousViolation.util.AnalysisConfig;
import org.spotter.ext.detection.continuousViolation.util.Bucket;
import org.spotter.ext.detection.utils.Utils;
/**
* Analyzes continuous performance requirement violation by iterating over
* buckets.
*
* @author Alexander Wert
*
*/
public class BucketStrategy implements IViolationAnalysisStrategy {
private static final int NUM_COARSE_GRAINED_BUCKETS = 3;
private static final double EPSILON_PERCENTAGE = 0.5;
@Override
public boolean analyze(NumericPairList<Long, Double> responsetimeSeries, AnalysisConfig analysisConfig,
double perfReqThreshold, double perfReqConfidence) {
boolean bucketsCovered = checkBucketCoverage(responsetimeSeries, analysisConfig, perfReqThreshold,
perfReqConfidence);
boolean equallyDistributed = checkEqualDistribution(responsetimeSeries);
return equallyDistributed && bucketsCovered;
}
private boolean checkEqualDistribution(NumericPairList<Long, Double> responsetimeSeries) {
double overallMean = LpeNumericUtils.average(responsetimeSeries.getValueArrayAsDouble());
long minTimestamp = responsetimeSeries.getKeyMin();
long maxTimestamp = responsetimeSeries.getKeyMax();
long step = ((maxTimestamp - minTimestamp) / NUM_COARSE_GRAINED_BUCKETS) + 1L;
long nextBorder = minTimestamp + step;
List<Double> values = new ArrayList<>();
for (NumericPair<Long, Double> pair : responsetimeSeries) {
if (pair.getKey() > nextBorder) {
if (!checkPartMean(overallMean, values)) {
return false;
}
nextBorder += step;
values.clear();
}
values.add(pair.getValue());
}
return checkPartMean(overallMean, values);
}
private boolean checkPartMean(double overallMean, List<Double> values) {
double tmpMean = LpeNumericUtils.average(values);
return tmpMean >= (overallMean - overallMean * EPSILON_PERCENTAGE)
&& tmpMean <= (overallMean + overallMean * EPSILON_PERCENTAGE);
}
private boolean checkBucketCoverage(NumericPairList<Long, Double> responsetimeSeries,
AnalysisConfig analysisConfig, double perfReqThreshold, double perfReqConfidence) {
List<Bucket> buckets = new ArrayList<Bucket>();
long bucketStart = Long.MIN_VALUE;
long timestamp;
NumericPairList<Long, Double> bucketSeries = null;
long bucketStep = Math.max(3000, Utils.meanInterRequestTime(responsetimeSeries) * 50);
for (int i = 0; i < responsetimeSeries.size(); i++) {
timestamp = responsetimeSeries.get(i).getKey();
if (timestamp > bucketStart + bucketStep) {
// new bucket started
if (bucketSeries != null) {
// analyze previous bucket
List<Double> responseTimes = bucketSeries.getValueListAsDouble();
int reqViolationsCount = countRequirementViolations(perfReqThreshold, responseTimes);
double percentageViolations = ((double) reqViolationsCount) / ((double) responseTimes.size());
Bucket bucket = new Bucket();
bucket.setStartTimestamp(bucketSeries.getKeyMin());
bucket.setEndTimestamp(bucketSeries.getKeyMax());
bucket.setRequirementViolated(percentageViolations > 1.0 - perfReqConfidence);
buckets.add(bucket);
}
bucketSeries = new NumericPairList<>();
bucketStart = timestamp;
}
bucketSeries.add(responsetimeSeries.get(i));
}
if (bucketSeries != null && bucketSeries.size() > 0) {
// analyze previous bucket
List<Double> responseTimes = bucketSeries.getValueListAsDouble();
int reqViolationsCount = countRequirementViolations(perfReqThreshold, responseTimes);
double percentageViolations = ((double) reqViolationsCount) / ((double) responseTimes.size());
Bucket bucket = new Bucket();
bucket.setStartTimestamp(bucketSeries.getKeyMin());
bucket.setEndTimestamp(bucketSeries.getKeyMax());
bucket.setRequirementViolated(percentageViolations > 1.0 - perfReqConfidence);
buckets.add(bucket);
}
int numViolatingBuckets = countViolatingBuckets(buckets);
return ((double) numViolatingBuckets) / ((double) buckets.size()) > (analysisConfig.getMinBucketTimeProportion());
}
private int countRequirementViolations(double perfReqThreshold, List<Double> responseTimes) {
int count = 0;
for (Double rt : responseTimes) {
if (rt > perfReqThreshold) {
count++;
}
}
return count;
}
private int countViolatingBuckets(List<Bucket> buckets) {
int count = 0;
for (Bucket b : buckets) {
if (b.isRequirementViolated()) {
count++;
}
}
return count;
}
}